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