]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/boost/boost/histogram/accumulators/sum.hpp
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / boost / boost / histogram / accumulators / sum.hpp
index 8ddd873bedca8cb94013e77e390d5413f0343b03..d1577f0ba6a07facb7a8c280b80ccb8048df8aae 100644 (file)
 
 #include <boost/core/nvp.hpp>
 #include <boost/histogram/fwd.hpp> // for sum<>
-#include <cmath>
-#include <type_traits>
+#include <cmath>                   // std::abs
+#include <type_traits>             // std::is_floating_point, std::common_type
 
 namespace boost {
 namespace histogram {
 namespace accumulators {
 
 /**
-  Uses Neumaier algorithm to compute accurate sums.
+  Uses Neumaier algorithm to compute accurate sums of floats.
 
-  The algorithm uses memory for two floats and is three to
-  five times slower compared to a simple floating point
-  number used to accumulate a sum, but the relative error
-  of the sum is at the level of the machine precision,
-  independent of the number of samples.
+  The algorithm is an improved Kahan algorithm
+  (https://en.wikipedia.org/wiki/Kahan_summation_algorithm). The algorithm uses memory for
+  two numbers and is three to five times slower compared to using a single number to
+  accumulate a sum, but the relative error of the sum is at the level of the machine
+  precision, independent of the number of samples.
 
-  A. Neumaier, Zeitschrift fuer Angewandte Mathematik
-  und Mechanik 54 (1974) 39-51.
+  A. Neumaier, Zeitschrift fuer Angewandte Mathematik und Mechanik 54 (1974) 39-51.
 */
-template <typename RealType>
+template <class ValueType>
 class sum {
+  static_assert(std::is_floating_point<ValueType>::value,
+                "ValueType must be a floating point type");
+
 public:
+  using value_type = ValueType;
+  using const_reference = const value_type&;
+
   sum() = default;
 
-  /// Initialize sum to value
-  explicit sum(const RealType& value) noexcept : large_(value) {}
+  /// Initialize sum to value and allow implicit conversion
+  sum(const_reference value) noexcept : sum(value, 0) {}
 
-  /// Set sum to value
-  sum& operator=(const RealType& value) noexcept {
-    large_ = value;
-    small_ = 0;
-    return *this;
-  }
+  /// Allow implicit conversion from sum<T>
+  template <class T>
+  sum(const sum<T>& s) noexcept : sum(s.large(), s.small()) {}
+
+  /// Initialize sum explicitly with large and small parts
+  sum(const_reference large, const_reference small) noexcept
+      : large_(large), small_(small) {}
 
   /// Increment sum by one
-  sum& operator++() { return operator+=(1); }
+  sum& operator++() noexcept { return operator+=(1); }
 
   /// Increment sum by value
-  sum& operator+=(const RealType& value) {
-    auto temp = large_ + value; // prevent optimization
-    if (std::abs(large_) >= std::abs(value))
-      small_ += (large_ - temp) + value;
-    else
-      small_ += (value - temp) + large_;
-    large_ = temp;
+  sum& operator+=(const_reference value) noexcept {
+    // prevent compiler optimization from destroying the algorithm
+    // when -ffast-math is enabled
+    volatile value_type l;
+    value_type s;
+    if (std::abs(large_) >= std::abs(value)) {
+      l = large_;
+      s = value;
+    } else {
+      l = value;
+      s = large_;
+    }
+    large_ += value;
+    l -= large_;
+    l += s;
+    small_ += l;
+    return *this;
+  }
+
+  /// Add another sum
+  sum& operator+=(const sum& s) noexcept {
+    operator+=(s.large_);
+    small_ += s.small_;
     return *this;
   }
 
   /// Scale by value
-  sum& operator*=(const RealType& value) {
+  sum& operator*=(const_reference value) noexcept {
     large_ *= value;
     small_ *= value;
     return *this;
   }
 
-  template <class T>
-  bool operator==(const sum<T>& rhs) const noexcept {
-    return large_ == rhs.large_ && small_ == rhs.small_;
+  bool operator==(const sum& rhs) const noexcept {
+    return large_ + small_ == rhs.large_ + rhs.small_;
   }
 
-  template <class T>
-  bool operator!=(const T& rhs) const noexcept {
-    return !operator==(rhs);
-  }
+  bool operator!=(const sum& rhs) const noexcept { return !operator==(rhs); }
+
+  /// Return value of the sum.
+  value_type value() const noexcept { return large_ + small_; }
 
   /// Return large part of the sum.
-  const RealType& large() const { return large_; }
+  const_reference large() const noexcept { return large_; }
 
   /// Return small part of the sum.
-  const RealType& small() const { return small_; }
+  const_reference small() const noexcept { return small_; }
 
-  // allow implicit conversion to RealType
-  operator RealType() const { return large_ + small_; }
+  // lossy conversion to value type must be explicit
+  explicit operator value_type() const noexcept { return value(); }
 
   template <class Archive>
   void serialize(Archive& ar, unsigned /* version */) {
@@ -89,9 +110,55 @@ public:
     ar& make_nvp("small", small_);
   }
 
+  // begin: extra operators to make sum behave like a regular number
+
+  sum& operator*=(const sum& rhs) noexcept {
+    const auto scale = static_cast<value_type>(rhs);
+    large_ *= scale;
+    small_ *= scale;
+    return *this;
+  }
+
+  sum operator*(const sum& rhs) const noexcept {
+    sum s = *this;
+    s *= rhs;
+    return s;
+  }
+
+  sum& operator/=(const sum& rhs) noexcept {
+    const auto scale = 1.0 / static_cast<value_type>(rhs);
+    large_ *= scale;
+    small_ *= scale;
+    return *this;
+  }
+
+  sum operator/(const sum& rhs) const noexcept {
+    sum s = *this;
+    s /= rhs;
+    return s;
+  }
+
+  bool operator<(const sum& rhs) const noexcept {
+    return operator value_type() < rhs.operator value_type();
+  }
+
+  bool operator>(const sum& rhs) const noexcept {
+    return operator value_type() > rhs.operator value_type();
+  }
+
+  bool operator<=(const sum& rhs) const noexcept {
+    return operator value_type() <= rhs.operator value_type();
+  }
+
+  bool operator>=(const sum& rhs) const noexcept {
+    return operator value_type() >= rhs.operator value_type();
+  }
+
+  // end: extra operators
+
 private:
-  RealType large_ = RealType();
-  RealType small_ = RealType();
+  value_type large_{};
+  value_type small_{};
 };
 
 } // namespace accumulators