GeographicLib
2.7
Toggle main menu visibility
Loading...
Searching...
No Matches
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) <karney@alum.mit.edu> 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
13
#include <
GeographicLib/Constants.hpp
>
14
15
namespace
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>
40
class
GEOGRAPHICLIB_EXPORT
Accumulator
{
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 × \e T is exactly representable as a \e T
141
* (i.e., ± powers of two). Use \e n = −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
**********************************************************************/
152
Accumulator
&
operator*=
(T y) {
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
**********************************************************************/
164
Accumulator
&
remainder
(T y) {
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
Constants.hpp
Header for GeographicLib::Constants class.
GEOGRAPHICLIB_EXPORT
#define GEOGRAPHICLIB_EXPORT
Definition
Constants.hpp:59
GEOGRAPHICLIB_VOLATILE
#define GEOGRAPHICLIB_VOLATILE
Definition
Math.hpp:64
GeographicLib::Accumulator::operator()
T operator()(T y) const
Definition
Accumulator.hpp:125
GeographicLib::Accumulator::operator=
Accumulator & operator=(T y)
Definition
Accumulator.hpp:111
GeographicLib::Accumulator::Accumulator
Accumulator(T y=T(0))
Definition
Accumulator.hpp:102
GeographicLib::Accumulator::operator>=
bool operator>=(T y) const
Definition
Accumulator.hpp:193
GeographicLib::Accumulator::operator-=
Accumulator & operator-=(T y)
Definition
Accumulator.hpp:137
GeographicLib::Accumulator::operator*=
Accumulator & operator*=(T y)
Definition
Accumulator.hpp:152
GeographicLib::Accumulator::operator!=
bool operator!=(T y) const
Definition
Accumulator.hpp:177
GeographicLib::Accumulator::operator>
bool operator>(T y) const
Definition
Accumulator.hpp:189
GeographicLib::Accumulator::operator+=
Accumulator & operator+=(T y)
Definition
Accumulator.hpp:131
GeographicLib::Accumulator::operator<=
bool operator<=(T y) const
Definition
Accumulator.hpp:185
GeographicLib::Accumulator::operator<
bool operator<(T y) const
Definition
Accumulator.hpp:181
GeographicLib::Accumulator::operator==
bool operator==(T y) const
Definition
Accumulator.hpp:173
GeographicLib::Accumulator::remainder
Accumulator & remainder(T y)
Definition
Accumulator.hpp:164
GeographicLib::Accumulator::operator*=
Accumulator & operator*=(int n)
Definition
Accumulator.hpp:145
GeographicLib::Accumulator::operator()
T operator()() const
Definition
Accumulator.hpp:117
GeographicLib::Math::sum
static T sum(T u, T v, T &t)
Definition
Math.cpp:55
GeographicLib
Namespace for GeographicLib.
Definition
Accumulator.cpp:12
include
GeographicLib
Accumulator.hpp
Generated by
1.17.0