Accumulator.hpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /**
  2. * \file Accumulator.hpp
  3. * \brief Header for GeographicLib::Accumulator class
  4. *
  5. * Copyright (c) Charles Karney (2010-2020) <charles@karney.com> and licensed
  6. * under the MIT/X11 License. For more information, see
  7. * https://geographiclib.sourceforge.io/
  8. **********************************************************************/
  9. #if !defined(GEOGRAPHICLIB_ACCUMULATOR_HPP)
  10. #define GEOGRAPHICLIB_ACCUMULATOR_HPP 1
  11. #include <GeographicLib/Constants.hpp>
  12. namespace GeographicLib {
  13. /**
  14. * \brief An accumulator for sums
  15. *
  16. * This allows many numbers of floating point type \e T to be added together
  17. * with twice the normal precision. Thus if \e T is double, the effective
  18. * precision of the sum is 106 bits or about 32 decimal places.
  19. *
  20. * The implementation follows J. R. Shewchuk,
  21. * <a href="https://doi.org/10.1007/PL00009321"> Adaptive Precision
  22. * Floating-Point Arithmetic and Fast Robust Geometric Predicates</a>,
  23. * Discrete & Computational Geometry 18(3) 305--363 (1997).
  24. *
  25. * Approximate timings (summing a vector<double>)
  26. * - double: 2ns
  27. * - Accumulator<double>: 23ns
  28. *
  29. * In the documentation of the member functions, \e sum stands for the value
  30. * currently held in the accumulator.
  31. *
  32. * Example of use:
  33. * \include example-Accumulator.cpp
  34. **********************************************************************/
  35. template<typename T = Math::real>
  36. class GEOGRAPHICLIB_EXPORT Accumulator {
  37. private:
  38. // _s + _t accumulators for the sum.
  39. T _s, _t;
  40. // Same as Math::sum, but requires abs(u) >= abs(v). This isn't currently
  41. // used.
  42. static T fastsum(T u, T v, T& t) {
  43. GEOGRAPHICLIB_VOLATILE T s = u + v;
  44. GEOGRAPHICLIB_VOLATILE T vp = s - u;
  45. t = v - vp;
  46. return s;
  47. }
  48. void Add(T y) {
  49. // Here's Shewchuk's solution...
  50. T u; // hold exact sum as [s, t, u]
  51. // Accumulate starting at least significant end
  52. y = Math::sum(y, _t, u);
  53. _s = Math::sum(y, _s, _t);
  54. // Start is _s, _t decreasing and non-adjacent. Sum is now (s + t + u)
  55. // exactly with s, t, u non-adjacent and in decreasing order (except for
  56. // possible zeros). The following code tries to normalize the result.
  57. // Ideally, we want _s = round(s+t+u) and _u = round(s+t+u - _s). The
  58. // following does an approximate job (and maintains the decreasing
  59. // non-adjacent property). Here are two "failures" using 3-bit floats:
  60. //
  61. // Case 1: _s is not equal to round(s+t+u) -- off by 1 ulp
  62. // [12, -1] - 8 -> [4, 0, -1] -> [4, -1] = 3 should be [3, 0] = 3
  63. //
  64. // Case 2: _s+_t is not as close to s+t+u as it shold be
  65. // [64, 5] + 4 -> [64, 8, 1] -> [64, 8] = 72 (off by 1)
  66. // should be [80, -7] = 73 (exact)
  67. //
  68. // "Fixing" these problems is probably not worth the expense. The
  69. // representation inevitably leads to small errors in the accumulated
  70. // values. The additional errors illustrated here amount to 1 ulp of the
  71. // less significant word during each addition to the Accumulator and an
  72. // additional possible error of 1 ulp in the reported sum.
  73. //
  74. // Incidentally, the "ideal" representation described above is not
  75. // canonical, because _s = round(_s + _t) may not be true. For example,
  76. // with 3-bit floats:
  77. //
  78. // [128, 16] + 1 -> [160, -16] -- 160 = round(145).
  79. // But [160, 0] - 16 -> [128, 16] -- 128 = round(144).
  80. //
  81. if (_s == 0) // This implies t == 0,
  82. _s = u; // so result is u
  83. else
  84. _t += u; // otherwise just accumulate u to t.
  85. }
  86. T Sum(T y) const {
  87. Accumulator a(*this);
  88. a.Add(y);
  89. return a._s;
  90. }
  91. public:
  92. /**
  93. * Construct from a \e T. This is not declared explicit, so that you can
  94. * write <code>Accumulator<double> a = 5;</code>.
  95. *
  96. * @param[in] y set \e sum = \e y.
  97. **********************************************************************/
  98. Accumulator(T y = T(0)) : _s(y), _t(0) {
  99. static_assert(!std::numeric_limits<T>::is_integer,
  100. "Accumulator type is not floating point");
  101. }
  102. /**
  103. * Set the accumulator to a number.
  104. *
  105. * @param[in] y set \e sum = \e y.
  106. **********************************************************************/
  107. Accumulator& operator=(T y) { _s = y; _t = 0; return *this; }
  108. /**
  109. * Return the value held in the accumulator.
  110. *
  111. * @return \e sum.
  112. **********************************************************************/
  113. T operator()() const { return _s; }
  114. /**
  115. * Return the result of adding a number to \e sum (but don't change \e
  116. * sum).
  117. *
  118. * @param[in] y the number to be added to the sum.
  119. * @return \e sum + \e y.
  120. **********************************************************************/
  121. T operator()(T y) const { return Sum(y); }
  122. /**
  123. * Add a number to the accumulator.
  124. *
  125. * @param[in] y set \e sum += \e y.
  126. **********************************************************************/
  127. Accumulator& operator+=(T y) { Add(y); return *this; }
  128. /**
  129. * Subtract a number from the accumulator.
  130. *
  131. * @param[in] y set \e sum -= \e y.
  132. **********************************************************************/
  133. Accumulator& operator-=(T y) { Add(-y); return *this; }
  134. /**
  135. * Multiply accumulator by an integer. To avoid loss of accuracy, use only
  136. * integers such that \e n &times; \e T is exactly representable as a \e T
  137. * (i.e., &plusmn; powers of two). Use \e n = &minus;1 to negate \e sum.
  138. *
  139. * @param[in] n set \e sum *= \e n.
  140. **********************************************************************/
  141. Accumulator& operator*=(int n) { _s *= n; _t *= n; return *this; }
  142. /**
  143. * Multiply accumulator by a number. The fma (fused multiply and add)
  144. * instruction is used (if available) in order to maintain accuracy.
  145. *
  146. * @param[in] y set \e sum *= \e y.
  147. **********************************************************************/
  148. Accumulator& operator*=(T y) {
  149. using std::fma;
  150. T d = _s; _s *= y;
  151. d = fma(y, d, -_s); // the error in the first multiplication
  152. _t = fma(y, _t, d); // add error to the second term
  153. return *this;
  154. }
  155. /**
  156. * Reduce accumulator to the range [-y/2, y/2].
  157. *
  158. * @param[in] y the modulus.
  159. **********************************************************************/
  160. Accumulator& remainder(T y) {
  161. using std::remainder;
  162. _s = remainder(_s, y);
  163. Add(0); // This renormalizes the result.
  164. return *this;
  165. }
  166. /**
  167. * Test equality of an Accumulator with a number.
  168. **********************************************************************/
  169. bool operator==(T y) const { return _s == y; }
  170. /**
  171. * Test inequality of an Accumulator with a number.
  172. **********************************************************************/
  173. bool operator!=(T y) const { return _s != y; }
  174. /**
  175. * Less operator on an Accumulator and a number.
  176. **********************************************************************/
  177. bool operator<(T y) const { return _s < y; }
  178. /**
  179. * Less or equal operator on an Accumulator and a number.
  180. **********************************************************************/
  181. bool operator<=(T y) const { return _s <= y; }
  182. /**
  183. * Greater operator on an Accumulator and a number.
  184. **********************************************************************/
  185. bool operator>(T y) const { return _s > y; }
  186. /**
  187. * Greater or equal operator on an Accumulator and a number.
  188. **********************************************************************/
  189. bool operator>=(T y) const { return _s >= y; }
  190. };
  191. } // namespace GeographicLib
  192. #endif // GEOGRAPHICLIB_ACCUMULATOR_HPP