1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
12 #include <rtl/math.hxx>
16 * This class provides LO with Kahan summation algorithm
17 * About this algorithm: https://en.wikipedia.org/wiki/Kahan_summation_algorithm
18 * For general purpose software we assume first order error is enough.
20 * Additionally queue and remember the last recent non-zero value and add it
21 * similar to approxAdd() when obtaining the final result to further eliminate
22 * accuracy errors. (e.g. for the dreaded 0.1 + 0.2 - 0.3 != 0.0)
28 constexpr KahanSum() = default;
30 constexpr KahanSum(double x_0
)
35 constexpr KahanSum(double x_0
, double err_0
)
41 constexpr KahanSum(const KahanSum
& fSum
) = default;
45 * Adds a value to the sum using Kahan summation.
59 double t
= m_fSum
+ m_fMem
;
60 if (std::abs(m_fSum
) >= std::abs(m_fMem
))
61 m_fError
+= (m_fSum
- t
) + m_fMem
;
63 m_fError
+= (m_fMem
- t
) + m_fSum
;
69 * Adds a value to the sum using Kahan summation.
72 inline void add(const KahanSum
& fSum
)
80 * Substracts a value to the sum using Kahan summation.
83 inline void subtract(const KahanSum
& fSum
)
91 constexpr KahanSum
operator-() const
94 fKahanSum
.m_fSum
= -m_fSum
;
95 fKahanSum
.m_fError
= -m_fError
;
96 fKahanSum
.m_fMem
= -m_fMem
;
100 constexpr KahanSum
& operator=(double fSum
)
108 constexpr KahanSum
& operator=(const KahanSum
& fSum
) = default;
110 inline void operator+=(const KahanSum
& fSum
) { add(fSum
); }
112 inline void operator+=(double fSum
) { add(fSum
); }
114 inline void operator-=(const KahanSum
& fSum
) { subtract(fSum
); }
116 inline void operator-=(double fSum
) { add(-fSum
); }
118 inline KahanSum
operator+(double fSum
) const
120 KahanSum
fNSum(*this);
125 inline KahanSum
operator+(const KahanSum
& fSum
) const
127 KahanSum
fNSum(*this);
132 inline KahanSum
operator-(double fSum
) const
134 KahanSum
fNSum(*this);
139 inline KahanSum
operator-(const KahanSum
& fSum
) const
141 KahanSum
fNSum(*this);
147 * In some parts of the code of interpr_.cxx this may be used for
148 * product instead of sum. This operator shall be used for that task.
150 constexpr void operator*=(double fTimes
)
154 m_fSum
= get() * fTimes
;
159 m_fSum
= (m_fSum
+ m_fError
) * fTimes
;
164 constexpr void operator/=(double fDivides
)
168 m_fSum
= get() / fDivides
;
173 m_fSum
= (m_fSum
+ m_fError
) / fDivides
;
178 inline KahanSum
operator*(const KahanSum
& fTimes
) const { return get() * fTimes
.get(); }
180 inline KahanSum
operator*(double fTimes
) const { return get() * fTimes
; }
182 inline KahanSum
operator/(const KahanSum
& fDivides
) const { return get() / fDivides
.get(); }
184 inline KahanSum
operator/(double fDivides
) const { return get() / fDivides
; }
186 inline bool operator<(const KahanSum
& fSum
) const { return get() < fSum
.get(); }
188 inline bool operator<(double fSum
) const { return get() < fSum
; }
190 inline bool operator>(const KahanSum
& fSum
) const { return get() > fSum
.get(); }
192 inline bool operator>(double fSum
) const { return get() > fSum
; }
194 inline bool operator<=(const KahanSum
& fSum
) const { return get() <= fSum
.get(); }
196 inline bool operator<=(double fSum
) const { return get() <= fSum
; }
198 inline bool operator>=(const KahanSum
& fSum
) const { return get() >= fSum
.get(); }
200 inline bool operator>=(double fSum
) const { return get() >= fSum
; }
202 inline bool operator==(const KahanSum
& fSum
) const { return get() == fSum
.get(); }
204 inline bool operator!=(const KahanSum
& fSum
) const { return get() != fSum
.get(); }
208 * Returns the final sum.
213 const double fTotal
= m_fSum
+ m_fError
;
217 // Check the same condition as rtl::math::approxAdd() and if true
218 // return 0.0, if false use another Kahan summation adding m_fMem.
219 if (((m_fMem
< 0.0 && fTotal
> 0.0) || (fTotal
< 0.0 && m_fMem
> 0.0))
220 && rtl::math::approxEqual(m_fMem
, -fTotal
))
222 /* TODO: should we reset all values to zero here for further
223 * summation, or is it better to keep them as they are? */
227 // The actual argument passed to add() here does not matter as long as
228 // it is not 0, m_fMem is not 0 and will be added anyway, see add().
229 const_cast<KahanSum
*>(this)->add(m_fMem
);
230 const_cast<KahanSum
*>(this)->m_fMem
= 0.0;
231 return m_fSum
+ m_fError
;
240 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */