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>
18 // Checkout available optimization options.
19 // Note that it turned out to be problematic to support CPU-specific code
20 // that's not guaranteed to be available on that specific platform (see
21 // git commit 2d36e7f5186ba5215f2b228b98c24520bd4f2882). SSE2 is guaranteed on
22 // x86_64 and it is our baseline requirement for x86 on Windows, so SSE2 use is
23 // hardcoded on those platforms.
24 // Whenever we raise baseline to e.g. AVX, this may get
25 // replaced with AVX code (get it from mentioned git commit).
26 // Do it similarly with other platforms.
27 #if defined(X86_64) || (defined(INTEL) && defined(_WIN32))
29 KahanSum
executeSSE2(size_t& i
, size_t nSize
, const double* pCurrent
);
36 * This class provides LO with Kahan summation algorithm
37 * About this algorithm: https://en.wikipedia.org/wiki/Kahan_summation_algorithm
38 * For general purpose software we assume first order error is enough.
40 * Additionally queue and remember the last recent non-zero value and add it
41 * similar to approxAdd() when obtaining the final result to further eliminate
42 * accuracy errors. (e.g. for the dreaded 0.1 + 0.2 - 0.3 != 0.0)
48 constexpr KahanSum() = default;
50 constexpr KahanSum(double x_0
)
55 constexpr KahanSum(double x_0
, double err_0
)
61 constexpr KahanSum(const KahanSum
& fSum
) = default;
65 * Performs one step of the Neumaier sum of doubles.
66 * Overwrites the summand and error.
67 * T could be double or long double.
69 template <typename T
> static inline void sumNeumaierNormal(T
& sum
, T
& err
, const double& value
)
72 if (std::abs(sum
) >= std::abs(value
))
73 err
+= (sum
- t
) + value
;
75 err
+= (value
- t
) + sum
;
80 * Adds a value to the sum using Kahan summation.
94 sumNeumaierNormal(m_fSum
, m_fError
, m_fMem
);
99 * Adds a value to the sum using Kahan summation.
102 inline void add(const KahanSum
& fSum
)
105 add(fSum
.m_fSum
+ fSum
.m_fError
);
108 // Without SSE2 the sum+compensation value fails badly. Continue
109 // keeping the old though slightly off (see tdf#156985) explicit
110 // addition of the compensation value.
111 double sum
= fSum
.m_fSum
;
112 double err
= fSum
.m_fError
;
113 if (fSum
.m_fMem
!= 0.0)
114 sumNeumaierNormal(sum
, err
, fSum
.m_fMem
);
121 * Substracts a value to the sum using Kahan summation.
124 inline void subtract(const KahanSum
& fSum
) { add(-fSum
); }
127 constexpr KahanSum
operator-() const
130 fKahanSum
.m_fSum
= -m_fSum
;
131 fKahanSum
.m_fError
= -m_fError
;
132 fKahanSum
.m_fMem
= -m_fMem
;
136 constexpr KahanSum
& operator=(double fSum
)
144 constexpr KahanSum
& operator=(const KahanSum
& fSum
) = default;
146 inline void operator+=(const KahanSum
& fSum
) { add(fSum
); }
148 inline void operator+=(double fSum
) { add(fSum
); }
150 inline void operator-=(const KahanSum
& fSum
) { subtract(fSum
); }
152 inline void operator-=(double fSum
) { add(-fSum
); }
154 inline KahanSum
operator+(double fSum
) const
156 KahanSum
fNSum(*this);
161 inline KahanSum
operator+(const KahanSum
& fSum
) const
163 KahanSum
fNSum(*this);
168 inline KahanSum
operator-(double fSum
) const
170 KahanSum
fNSum(*this);
175 inline KahanSum
operator-(const KahanSum
& fSum
) const
177 KahanSum
fNSum(*this);
183 * In some parts of the code of interpr_.cxx this may be used for
184 * product instead of sum. This operator shall be used for that task.
186 constexpr void operator*=(double fTimes
)
190 m_fSum
= get() * fTimes
;
195 m_fSum
= (m_fSum
+ m_fError
) * fTimes
;
200 constexpr void operator/=(double fDivides
)
204 m_fSum
= get() / fDivides
;
209 m_fSum
= (m_fSum
+ m_fError
) / fDivides
;
214 inline KahanSum
operator*(const KahanSum
& fTimes
) const { return get() * fTimes
.get(); }
216 inline KahanSum
operator*(double fTimes
) const { return get() * fTimes
; }
218 inline KahanSum
operator/(const KahanSum
& fDivides
) const { return get() / fDivides
.get(); }
220 inline KahanSum
operator/(double fDivides
) const { return get() / fDivides
; }
222 inline bool operator<(const KahanSum
& fSum
) const { return get() < fSum
.get(); }
224 inline bool operator<(double fSum
) const { return get() < fSum
; }
226 inline bool operator>(const KahanSum
& fSum
) const { return get() > fSum
.get(); }
228 inline bool operator>(double fSum
) const { return get() > fSum
; }
230 inline bool operator<=(const KahanSum
& fSum
) const { return get() <= fSum
.get(); }
232 inline bool operator<=(double fSum
) const { return get() <= fSum
; }
234 inline bool operator>=(const KahanSum
& fSum
) const { return get() >= fSum
.get(); }
236 inline bool operator>=(double fSum
) const { return get() >= fSum
; }
238 inline bool operator==(const KahanSum
& fSum
) const { return get() == fSum
.get(); }
240 inline bool operator!=(const KahanSum
& fSum
) const { return get() != fSum
.get(); }
244 * Returns the final sum.
249 const double fTotal
= m_fSum
+ m_fError
;
253 // Check the same condition as rtl::math::approxAdd() and if true
254 // return 0.0, if false use another Kahan summation adding m_fMem.
255 if (((m_fMem
< 0.0 && fTotal
> 0.0) || (fTotal
< 0.0 && m_fMem
> 0.0))
256 && rtl::math::approxEqual(m_fMem
, -fTotal
))
258 /* TODO: should we reset all values to zero here for further
259 * summation, or is it better to keep them as they are? */
263 // The actual argument passed to add() here does not matter as long as
264 // it is not 0, m_fMem is not 0 and will be added anyway, see add().
265 const_cast<KahanSum
*>(this)->add(m_fMem
);
266 const_cast<KahanSum
*>(this)->m_fMem
= 0.0;
267 return m_fSum
+ m_fError
;
276 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */