HILA
Loading...
Searching...
No Matches
extended.h
Go to the documentation of this file.
1#ifndef HILA_EXTENDED_H
2#define HILA_EXTENDED_H
3
4/**
5 * @file extended.h
6 * @brief This files containts definitions for the extended precision class that allows for high
7 * precision reductions using Kahan-Babushka-Klein summation.
8 *
9 * @details
10 * Uses internally three double precision variables to store the value and compensation. Precision
11 * can be > 15 orders of magnitude better than plain doble.
12 *
13 * Use:
14 * @code {.cpp}
15 *
16 * ExtendedPrecision s1 = 0, s2 = 0;
17 * onsites(ALL) {
18 * s1 += <double/float/int -valued expression>;
19 * s2 += <some other expression>
20 * }
21 *
22 * // below subtraction and comparison is done in extended precision
23 * if (s1 == s2)
24 * hila::out0 << "s1 equals s2\n";
25 * else
26 * hila::out0 << "s1 is " << (s1 > s2) ? "greater" : "less"
27 * << " than s2, with difference " << s1 - s2 << '\n';
28 * @endcode
29 *
30 * Class implements basic arithmetics ( + - * / ), assignments (= += *=) and
31 * comparison ops for ExtendedPrecision types. ExtendedPrecision is not
32 * automatically downgraded to double in order to avoid accidental loss of accuracy.
33 *
34 * To get a double approximation of the value use
35 * @code {.cpp}
36 * s1.to_double()
37 * // or equvalently explicit casting
38 * static_cast<double>(s1)
39 * @endcode
40 *
41 */
42
43
44#include "plumbing/defs.h"
45
46class ExtendedPrecision {
47
48 public:
49 double value;
50 double compensation;
51 double compensation2;
52
53 ExtendedPrecision() = default;
54 ~ExtendedPrecision() = default;
55#pragma hila loop_function
56 ExtendedPrecision(const ExtendedPrecision &rhs)
57 : value(rhs.value), compensation(rhs.compensation), compensation2(rhs.compensation2) {}
58 ExtendedPrecision(double v) : value(v), compensation(0), compensation2(0) {}
59 ExtendedPrecision(double v, double c, double c2)
60 : value(v), compensation(c), compensation2(c2) {}
61
62// Assignments are used in generated code, mark as loop functions
63#pragma hila loop_function
64 ExtendedPrecision &operator=(const ExtendedPrecision &rhs) {
65 if (this != &rhs) {
66 value = rhs.value;
67 compensation = rhs.compensation;
68 compensation2 = rhs.compensation2;
69 }
70 return *this;
71 }
72#pragma hila loop_function
73 template <typename T, std::enable_if_t<hila::is_arithmetic<T>::value, int> = 0>
74 ExtendedPrecision &operator=(const T &v) {
75 value = v;
76 compensation = compensation2 = 0;
77 return *this;
78 }
79
80
81 ExtendedPrecision operator+() const {
82 return *this;
83 }
84
85 // template <typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
86 // ExtendedPrecision operator+(const T &rhs) const {
87 // ExtendedPrecision temp(rhs);
88 // ExtendedPrecision result = *this;
89 // result += temp;
90 // return result;
91 // }
92
93 inline ExtendedPrecision operator-() const {
94 return ExtendedPrecision(-value, -compensation, -compensation2);
95 }
96
97 /**
98 * @brief += addition assignment operator
99 * @details Impmlements the Kahan-Babushka-Klein summation
100 *
101 * @param rhs - value to be summed
102 * @return ExtendedPrecision&
103 */
104
105#pragma hila loop_function
106 template <typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
107 inline const ExtendedPrecision &operator+=(const T &rhs_) {
108 double rhs = rhs_;
109 double t = value + rhs;
110 double c, cc;
111 if (abs(value) >= abs(rhs)) {
112 c = (value - t) + rhs;
113 } else {
114 c = (rhs - t) + value;
115 }
116 value = t;
117 t = compensation + c;
118 if (abs(compensation) >= abs(c)) {
119 cc = (compensation - t) + c;
120 } else {
121 cc = (c - t) + compensation;
122 }
123 compensation = t;
124 compensation2 += cc;
125
126 return *this;
127 }
128
129
130 /**
131 * @brief += addition assignment operator for two EPs
132 */
133
134#pragma hila loop_function
135 // need to take a copy to avoid problems with a += a
136 inline const ExtendedPrecision &operator+=(ExtendedPrecision rhs) {
137 *this += rhs.compensation2;
138 *this += rhs.compensation;
139 *this += rhs.value;
140 return *this;
141 }
142
143 inline const ExtendedPrecision &operator-=(const ExtendedPrecision &rhs) {
144 return *this += -rhs;
145 }
146
147 template <typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
148 inline const ExtendedPrecision &operator-=(const T &rhs) {
149 return *this += -rhs;
150 }
151
152 template <typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
153 inline const ExtendedPrecision &operator*=(T rhs) {
154 static_assert(std::is_integral<T>::value,
155 "ExtendedPrecision can be multiplied only by integral value without losing "
156 "precision! Convert to double for more general operations");
157
158 if (rhs == 0) {
159 *this = 0;
160 } else {
161 bool negative = (!std::is_unsigned<T>::value && (rhs < 0));
162 if (negative)
163 rhs = -rhs;
164
165 T mult = 1;
166 while (rhs % 2 == 0) {
167 rhs /= 2;
168 mult *= 2;
169 }
170
171 // it's safe to multiply by power of 2
172 this->value *= mult;
173 this->compensation *= mult;
174 this->compensation2 *= mult;
175
176 auto a = *this;
177 // the rest is done by addition
178 // we add a rhs-1 times to *this, multiplying it by rhs
179 // in any case, factorize rhs as 2^n + 2^m + .. + 1 (rhs is odd)
180 while (rhs > 1) {
181 mult = 1;
182 while (2 * mult < rhs) {
183 mult *= 2;
184 }
185 ExtendedPrecision b;
186 // again power of 2
187 b.value = a.value * mult;
188 b.compensation = a.compensation * mult;
189 b.compensation2 = a.compensation2 * mult;
190
191 *this += b;
192 rhs -= mult;
193 }
194 if (negative) {
195 this->value = -this->value;
196 this->compensation = -this->compensation;
197 this->compensation2 = -this->compensation2;
198 }
199 }
200 return *this;
201 }
202
203
204 // double double sum
205 // ExtendedPrecision &operator+=(const ExtendedPrecision &rhs) {
206
207 // ExtendedPrecision<T> temp = fast_two_sum((*this).value, rhs.value);
208 // (*this) = fast_two_sum(temp.value, temp.compensation + (*this).compensation);
209
210 // return *this;
211
212 // }
213
214 /**
215 * @brief Conversion back to double from ExtendedPrecision
216 * @details Converts the ExtendedPrecision object back to double by summing the compensation
217 * and returning. Marked explicit in order to avoid mistakenly losing precision
218 * @return double
219 */
220 explicit operator double() const {
221 return value + (compensation + compensation2);
222 }
223
224 /**
225 * @brief Returns the compensated value as double
226 */
227 double to_double() const {
228 return value + (compensation + compensation2);
229 }
230};
231
232/// template utilities: hila::is_extended<T>::value and
233/// hila::is_arithmetic_or_extended<T>::value
234
235namespace hila {
236template <typename T>
237struct is_extended : std::integral_constant<bool, std::is_same<T, ExtendedPrecision>::value> {};
238
239template <typename T>
240struct is_arithmetic_or_extended
241 : std::integral_constant<bool, hila::is_arithmetic<T>::value || hila::is_extended<T>::value> {};
242
243template <typename A, typename B>
244struct AorB_extended
245 : std::integral_constant<
246 bool, (hila::is_extended<A>::value && hila::is_arithmetic_or_extended<B>::value) ||
247 (hila::is_extended<B>::value && std::is_arithmetic<A>::value)> {};
248
249} // namespace hila
250
251
252/// ExtendedPrecision precision operators + - * / -- also ext * arithm.
253
254// use here copy constructor on purpose
255// +
256template <typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
257inline ExtendedPrecision operator+(ExtendedPrecision a, const T b) {
258 return a += b;
259}
260template <typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
261inline ExtendedPrecision operator+(const T b, ExtendedPrecision a) {
262 return a += b;
263}
264inline ExtendedPrecision operator+(ExtendedPrecision a, const ExtendedPrecision &b) {
265 return a += b;
266}
267
268// -
269template <typename A, typename B, std::enable_if_t<hila::AorB_extended<A, B>::value, int> = 0>
270inline ExtendedPrecision operator-(const A &a, const B &b) {
271 return a + (-b);
272}
273
274// *
275template <typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
276inline ExtendedPrecision operator*(ExtendedPrecision a, T v) {
277 static_assert(std::is_integral<T>::value,
278 "ExtendedPrecision can be multiplied only by integral value without losing "
279 "precision! Convert to double for more general operations");
280 return a *= v;
281}
282template <typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
283inline ExtendedPrecision operator*(T v, ExtendedPrecision a) {
284 static_assert(std::is_integral<T>::value,
285 "ExtendedPrecision can be multiplied only by integral value without losing "
286 "precision! Convert to double for more general operations");
287 return a *= v;
288}
289
290
291/// comparison operators
292
293// Values can be equal even if the .value and .compensation are not identical!
294// Identical values will satisfy the result below
295
296template <typename A, typename B, std::enable_if_t<hila::AorB_extended<A, B>::value, int> = 0>
297inline bool operator==(const A &a, const B &b) {
298 ExtendedPrecision tmp = a - b;
299 return (tmp.to_double() == 0.0);
300}
301template <typename A, typename B, std::enable_if_t<hila::AorB_extended<A, B>::value, int> = 0>
302inline bool operator!=(const A &a, const B &b) {
303 ExtendedPrecision tmp = a - b;
304 return (tmp.to_double() != 0.0);
305}
306template <typename A, typename B, std::enable_if_t<hila::AorB_extended<A, B>::value, int> = 0>
307inline bool operator>(const A &a, const B &b) {
308 ExtendedPrecision tmp = a - b;
309 return (tmp.to_double() > 0.0);
310}
311template <typename A, typename B, std::enable_if_t<hila::AorB_extended<A, B>::value, int> = 0>
312inline bool operator<(const A &a, const B &b) {
313 ExtendedPrecision tmp = a - b;
314 return (tmp.to_double() < 0.0);
315}
316template <typename A, typename B, std::enable_if_t<hila::AorB_extended<A, B>::value, int> = 0>
317inline bool operator>=(const A &a, const B &b) {
318 ExtendedPrecision tmp = a - b;
319 return (tmp.to_double() >= 0.0);
320}
321template <typename A, typename B, std::enable_if_t<hila::AorB_extended<A, B>::value, int> = 0>
322inline bool operator<=(const A &a, const B &b) {
323 ExtendedPrecision tmp = a - b;
324 return (tmp.to_double() <= 0.0);
325}
326
327
328inline std::ostream &operator<<(std::ostream &strm, const ExtendedPrecision &var) {
329 return strm << var.to_double();
330}
331
332namespace hila {
333
334template <typename Ntype>
335Ntype cast_to(const ExtendedPrecision &ep) {
336 if constexpr (std::is_same<Ntype, ExtendedPrecision>::value)
337 return ep;
338 else
339 return static_cast<Ntype>(ep.to_double());
340}
341
342inline std::string prettyprint(const ExtendedPrecision &val, int prec = 8) {
343 return prettyprint(val.to_double(), prec);
344}
345
346} // namespace hila
347
348
349#endif // EXTENDED_H
std::ostream & operator<<(std::ostream &strm, const Array< n, m, T > &A)
Stream operator.
Definition array.h:978
T abs(const Complex< T > &a)
Return absolute value of Complex number.
Definition cmplx.h:1266
This file defines all includes for HILA.
ExtendedPrecision operator+(ExtendedPrecision a, const T b)
ExtendedPrecision precision operators + - * / – also ext * arithm.
Definition extended.h:257
bool operator==(const A &a, const B &b)
comparison operators
Definition extended.h:297
void mult(const Mt1 &a, const Mt2 &b, Mt3 &res)
compute product of two matrices and write result to existing matrix
Definition matrix.h:2604
Implement hila::swap for gauge fields.
Definition array.h:982
Array< n, m, Ntype > cast_to(const Array< n, m, T > &mat)
Array casting operation.
Definition array.h:1292