Arcane  4.1.12.0
User documentation
Loading...
Searching...
No Matches
HPReal.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* HPReal.h (C) 2000-2019 */
9/* */
10/* High-precision real number. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCANE_UTILS_HPREAL_H
13#define ARCANE_UTILS_HPREAL_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16
17#include "arcane/utils/Numeric.h"
18
19#include <iosfwd>
20
21/*---------------------------------------------------------------------------*/
22/*---------------------------------------------------------------------------*/
23
24namespace Arcane
25{
26
27/*---------------------------------------------------------------------------*/
28/*---------------------------------------------------------------------------*/
29
30/*!
31 * \brief Class implementing a High-Precision real number.
32
33 This class is based on the article:
34
35 Obtaining identical results with double precision global accuracy on
36 different numbers of processors in parallel particle Monte Carlo
37 simulations (Mathew A. Cleveland, Thomas A. Brunner, Nicholas A. Gentile,
38 jeffrey A. Keasler) in Journal Of Computational Physics 251 (2013) 223-236.
39
40 Possible operations are accumulation (operator+=()),
41 reduction (reduce()) and conversion to a Real (toReal()).
42 Other operations such as division or multiplication are not possible.
43
44 To conform to the classic 'Real' type, the default constructor
45 of this class performs no initialization.
46
47 The toReal() method allows converting the HPReal into a classic Real.
48 The typical usage is as follows:
49
50 \code
51 * HPReal r(0.0);
52 * ArrayView<Real> x = ...;
53 * for( Integer i=0, n=x.size(); i<n; ++i ){
54 * // Accumulates the value
55 * r += x[i];
56 * }
57 * Real final_r = r.toReal();
58 \endcode
59
60 Evolution 12/18/18:
61
62 Correction of the base algorithm error for static HPReal _doTwoSum(Real a, Real b)
63
64 Real sum_error = (a - (value-approx_b) + (b-approx_b));
65
66 became (in accordance with Ref 1)
67
68 Real sum_error = (a - (value-approx_b)) + (b-approx_b);
69
70 Correction of the error in static HPReal _doQuickTwoSum(Real a,Real b)
71
72 it is necessary to test if a>b and invert a and b if this is not the case (see Ref 2, 3, 4)
73
74 Modification of Accumulate and Reduce,
75 Accumulate and Reduce were based on Ref 1, which is based on Ref 4
76
77 Addition of products as proposed in Ref 2 (in accordance with Ref 5)
78 of HPReal*Real or HPReal*HPReal (we take the same value of p for the
79 SPLIT() function as in Ref 2, 4, 5)
80
81 Note that the Real*Real product in Ref 4 (p.4) is not written in the same way as those in Ref 2 (p.2) or 5 (p.3)
82
83 +++++++++++++++++
84
85 Overall, reduction algorithms or errors accumulated only with addition are less good
86
87 Algorithm 6 of DD_TWOSUM [Ref 2] is also less good:
88
89 \code
90 static HPReal accumulate(Real a,MYHPReal b)
91 {
92 HPReal x(_doTwoSum(a,b.value()));
93 Real d= x.correction() + b.correction();
94 HPReal u(_doQuickTwoSum(x.value(),d));
95 return _doQuickTwoSum(u.value(),u.correction());
96 }
97 \endcode
98
99 or
100
101 \code
102 HPReal reduce(HPReal a,HPReal b)
103 {
104 HPReal x(_doTwoSum(a.value(),b.value()));
105 HPReal y(_doTwoSum(a.correction(),b.correction()));
106 Real d= x.correction() + y.value();
107 HPReal u(_doQuickTwoSum(x.value(),d));
108 Real w=y.correction()+u.correction();
109 return _doQuickTwoSum(u.value(),w);
110 }
111 \endcode
112
113 Tests on millions of positive as well as negative values have been performed
114 for addition, multiplication, and division in different orders.
115
116 I have not managed to show a difference in results in a simple case between the proposed algorithm for the sum and the one initially coded.
117
118 References
119 ----------
120
121 Ref 1
122 Obtaining identical results with double precision global accuracy on
123 different numbers of processors in parallel particle Monte Carlo
124 simulations (Mathew A. Cleveland, Thomas A. Brunner, Nicholas A. Gentile,
125 jeffrey A. Keasler) in Journal Of Computational Physics 251 (2013) 223-236.
126
127 Ref 2
128 Automatic Source-to-Source error compensation of floating-point programs
129 L.Thevenoux, Ph langlois, Mathieu Martel
130 HAL Id: hal-01158399
131
132 Ref 3
133 Numerical validation of compensated summation algorithms withs stochastic arithmetic
134 S.Gaillat, F.Jézéquel , R.Picot
135 Published in Electronic Notes in Theoritical Computer Science
136
137 or
138
139 Numerical validation of compensated algorithms withs stochastic arithmetic
140 S.Gaillat, F.Jézéquel , R.Picot
141 Published in Applied Mathematics and Computation 329 (2018)339-363
142
143 Ref 4
144 Library for Double-Double and Quad-Double Arithmetic
145 December 29, 2007
146 Yozo Hida Xiaoye Li D.H.Bailey
147
148 Ref 5
149 Accurate floating point Product and exponentation
150 S.Graillat
151 HAL ID: hal-00164607
152
153 Ref 6
154 A floating point technique for extending the avaible precision
155 T.J.Dekker
156 Numeri.Math 18, 224-242 (1971)
157*/
158class ARCANE_UTILS_EXPORT HPReal
159{
160 public:
161
162 /*!
163 * \brief Default constructor without initialization.
164 */
166
167 //! Creates an HP real with the value \a value and the correction \a correction
168 explicit HPReal(double avalue)
169 : m_value(avalue)
170 , m_correction(0.0)
171 {}
172
173 //! Creates an HP real with the value \a value and the correction \a correction
174 HPReal(double avalue, double acorrection)
175 : m_value(avalue)
176 , m_correction(acorrection)
177 {}
178
179 //! Internal value. Generally, you must use toReal()
180 Real value() const { return m_value; }
181
182 //! Internal correction.
183 Real correction() const { return m_correction; }
184
185 //! Adds a Real while preserving the error.
187 {
188 *this = accumulate(v, *this);
189 }
190
191 //! Adds an HPReal \a v while preserving the error (reduction)
192 inline void operator+=(HPReal v)
193 {
194 *this = reduce(*this, v);
195 }
196
197 //! Converts the instance to a Real.
198 inline Real toReal() const
199 {
200 return value() + correction();
201 }
202
203 //! Adds an HPReal \a v while preserving the error (reduction)
205 {
206 return reduce(*this, b);
207 }
208
209 //! Multiplies a Real while preserving the error.
211 {
212 *this = product(v, *this);
213 }
214
215 //! Multiplies an HPReal \a v while preserving the error (reduction)
216 inline void operator*=(HPReal v)
217 {
218 *this = product(*this, v);
219 }
220
221 //! Multiplies a Real while preserving the error.
223 {
224 *this = div2(v, *this);
225 }
226
227 //! Multiplies an HPReal \a v while preserving the error (reduction)
228 inline void operator/=(HPReal v)
229 {
230 *this = div2(*this, v);
231 }
232
233 /*!
234 * \brief Reads an HPReal from the stream \a i.
235 * The pair is read in the form of two #Real type values.
236 */
237 std::istream& assign(std::istream& i);
238 //! Writes the instance to the stream \a o readable by an assign()
239 std::ostream& print(std::ostream& o) const;
240 //! Writes the instance to the stream \a o in the form (x,y)
241 std::ostream& printPretty(std::ostream& o) const;
242
243 public:
244
245 //! Zero value.
246 static HPReal zero() { return HPReal(0.0); }
247
248 public:
249
250 static HPReal accumulate(Real a, HPReal b)
251 {
252 HPReal x(_doTwoSum(a, b.value()));
253 Real c = x.correction() + b.correction();
254 return _doQuickTwoSum(x.value(), c);
255 }
256
257 // Mpi passes through
258 static HPReal reduce(HPReal a, HPReal b)
259 {
260 HPReal x(_doTwoSum(a.value(), b.value()));
261 Real c = x.correction() + a.correction() + b.correction();
262 return _doQuickTwoSum(x.value(), c);
263 }
264
265 // algo 11 of AC_TWOPRODUCTS Ref 2
266 static HPReal product(HPReal a, HPReal b)
267 {
268 HPReal x(_doTwoProducts(a.value(), b.value()));
269 Real w = x.correction() + (a.value() * b.correction() + b.value() * a.correction());
270 return HPReal(x.value(), w);
271 }
272 // algo 11 of AC_TWOPRODUCTS Ref 2
273 static HPReal product(Real a, HPReal b)
274 {
275 HPReal x(_doTwoProducts(a, b.value()));
276 Real w = x.correction() + (a * b.correction());
277 return HPReal(x.value(), w);
278 }
279
280 // algo div2 Ref 6 div2 = a/b
281 // I put parentheses in the evaluation of w to force an order sometimes the compiler options
282 // ATTENTION the last 2 lines of the div2 algorithm are not included (we do not renormalize)
283 // The "mul2" algorithm also contains them and did not include them for "product" because algorithm 11 of Ref 2 does not have them (in my humble opinion)
284 static HPReal div2(HPReal a, HPReal b)
285 {
286 Real c = a.value() / b.value();
287 HPReal u(_doTwoProducts(c, b.value()));
288 Real w = ((((a.value() - u.value()) + -u.correction()) + a.correction()) - c * b.correction()) / b.value();
289 return HPReal(c, w);
290 }
291 // algo div2 Ref 6 div2 = a/b
292 static HPReal div2(Real b, HPReal a)
293 {
294 Real c = a.value() / b;
295 HPReal u(_doTwoProducts(c, b));
296 Real w = (((a.value() - u.value()) + -u.correction()) + a.correction()) / b;
297 return HPReal(c, w);
298 }
299
300 private:
301
302 Real m_value;
303 Real m_correction;
304
305 private:
306
307 // Correction of ()
308 static HPReal _doTwoSum(Real a, Real b)
309 {
310 Real value = a + b;
311 Real approx_b = value - a;
312 Real sum_error = (a - (value - approx_b)) + (b - approx_b);
313 return HPReal(value, sum_error);
314 }
315 // correction tests of the values of a and b and we put absolute values
316 static HPReal _doQuickTwoSum(Real a1, Real b1)
317 {
318 Real a = a1;
319 Real b = b1;
320 if (std::abs(b1) > std::abs(a1)) {
321 a = b1;
322 b = a1;
323 }
324 Real value = a + b;
325 Real error_value = (b - (value - a));
326 return HPReal(value, error_value);
327 }
328 // algorithm 4 ref 2 or algorithm 2.4 ref 3 or algorithm 6 p.4
329 static HPReal _doTwoProducts(Real a, Real b)
330 {
331 Real x = a * b;
332 HPReal aw = SPLIT(a);
333 HPReal bw = SPLIT(b);
334 Real y = aw.correction() * bw.correction() - (((x - aw.value() * bw.value()) - aw.correction() * bw.value()) - aw.value() * bw.correction());
335 return HPReal(x, y);
336 }
337 static HPReal SPLIT(Real a)
338 {
339 const Real f = 134217729; // 1+ 2^ceil(52/2);
340 Real c = f * a;
341 Real x = c - (c - a);
342 Real y = a - x;
343 return HPReal(x, y);
344 }
345};
346
347/*---------------------------------------------------------------------------*/
348/*---------------------------------------------------------------------------*/
349
350inline bool
351operator<(const HPReal& a, const HPReal& b)
352{
353 return a.toReal() < b.toReal();
354}
355
356inline bool
357operator>(const HPReal& a, const HPReal& b)
358{
359 return a.toReal() > b.toReal();
360}
361
362inline bool
363operator==(const HPReal& a, const HPReal& b)
364{
365 return a.value() == b.value() && a.correction() == b.correction();
366}
367
368inline bool
369operator!=(const HPReal& a, const HPReal& b)
370{
371 return !operator==(a, b);
372}
373
374inline HPReal
375operator+(const HPReal& a, const HPReal& b)
376{
377 return HPReal::reduce(a, b);
378}
379
380/*---------------------------------------------------------------------------*/
381/*---------------------------------------------------------------------------*/
382
383inline std::ostream&
384operator<<(std::ostream& o, HPReal t)
385{
386 return t.printPretty(o);
387}
388
389inline std::istream&
390operator>>(std::istream& i, HPReal& t)
391{
392 return t.assign(i);
393}
394
395/*---------------------------------------------------------------------------*/
396/*---------------------------------------------------------------------------*/
397
398} // End namespace Arcane
399
400/*---------------------------------------------------------------------------*/
401/*---------------------------------------------------------------------------*/
402
403#endif
Class implementing a High-Precision real number.
Definition HPReal.h:159
void operator*=(HPReal v)
Multiplies an HPReal v while preserving the error (reduction).
Definition HPReal.h:216
HPReal()
Default constructor without initialization.
Definition HPReal.h:165
static HPReal zero()
Zero value.
Definition HPReal.h:246
HPReal reduce(HPReal b) const
Adds an HPReal v while preserving the error (reduction).
Definition HPReal.h:204
Real toReal() const
Converts the instance to a Real.
Definition HPReal.h:198
void operator+=(Real v)
Adds a Real while preserving the error.
Definition HPReal.h:186
HPReal(double avalue)
Creates an HP real with the value value and the correction correction.
Definition HPReal.h:168
void operator/=(Real v)
Multiplies a Real while preserving the error.
Definition HPReal.h:222
void operator+=(HPReal v)
Adds an HPReal v while preserving the error (reduction).
Definition HPReal.h:192
HPReal(double avalue, double acorrection)
Creates an HP real with the value value and the correction correction.
Definition HPReal.h:174
Real correction() const
Internal correction.
Definition HPReal.h:183
void operator*=(Real v)
Multiplies a Real while preserving the error.
Definition HPReal.h:210
void operator/=(HPReal v)
Multiplies an HPReal v while preserving the error (reduction).
Definition HPReal.h:228
Real value() const
Internal value. Generally, you must use toReal().
Definition HPReal.h:180
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
bool operator<(const Item &item1, const Item &item2)
Compare two entities.
Definition Item.h:566
std::istream & operator>>(std::istream &istr, eItemKind &item_kind)
Input operator from a stream.
double Real
Type representing a real number.
std::ostream & operator<<(std::ostream &ostr, eItemKind item_kind)
Output operator for a stream.