LibreOffice Module sc (master)  1
arraysumfunctor.hxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  */
10 
11 #pragma once
12 
13 #include <cstdint>
14 #include <cmath>
15 
16 #include <sal/mathconf.h>
17 #include <sal/types.h>
18 #include <tools/simd.hxx>
19 #include <tools/cpuid.hxx>
20 #include <kahan.hxx>
21 
22 namespace sc
23 {
25 {
26 private:
27  const double* mpArray;
28  size_t mnSize;
29 
30 public:
31  ArraySumFunctor(const double* pArray, size_t nSize)
32  : mpArray(pArray)
33  , mnSize(nSize)
34  {
35  }
36 
38  {
39  const static bool hasSSE2 = cpuid::hasSSE2();
40 
41  KahanSum fSum = 0.0;
42  size_t i = 0;
43  const double* pCurrent = mpArray;
44 
45  if (hasSSE2)
46  {
47  while (i < mnSize && !simd::isAligned<double, 16>(pCurrent))
48  {
49  fSum += *pCurrent++;
50  i++;
51  }
52  if (i < mnSize)
53  {
54  fSum += executeSSE2(i, pCurrent);
55  }
56  }
57  else
58  fSum = executeUnrolled(i, pCurrent);
59 
60  // sum rest of the array
61 
62  for (; i < mnSize; ++i)
63  fSum += mpArray[i];
64 
65  // If the sum is a NaN, some of the terms were empty cells, probably.
66  // Re-calculate, carefully
67  double fVal = fSum.get();
68  if (!std::isfinite(fVal))
69  {
70  sal_uInt32 nErr = reinterpret_cast<sal_math_Double*>(&fVal)->nan_parts.fraction_lo;
71  if (nErr & 0xffff0000)
72  {
73  fSum = 0;
74  for (i = 0; i < mnSize; i++)
75  {
76  if (!std::isfinite(mpArray[i]))
77  {
78  nErr = reinterpret_cast<const sal_math_Double*>(&mpArray[i])
79  ->nan_parts.fraction_lo;
80  if (!(nErr & 0xffff0000))
81  fSum += mpArray[i]; // Let errors encoded as NaNs propagate ???
82  }
83  else
84  fSum += mpArray[i];
85  }
86  }
87  }
88  return fSum;
89  }
90 
91 private:
92  double executeSSE2(size_t& i, const double* pCurrent) const;
93  KahanSum executeUnrolled(size_t& i, const double* pCurrent) const
94  {
95  size_t nRealSize = mnSize - i;
96  size_t nUnrolledSize = nRealSize - (nRealSize % 4);
97 
98  if (nUnrolledSize > 0)
99  {
100  KahanSum sum0 = 0.0;
101  KahanSum sum1 = 0.0;
102  KahanSum sum2 = 0.0;
103  KahanSum sum3 = 0.0;
104 
105  for (; i < nUnrolledSize; i += 4)
106  {
107  sum0 += *pCurrent++;
108  sum1 += *pCurrent++;
109  sum2 += *pCurrent++;
110  sum3 += *pCurrent++;
111  }
112  // We are using pairwise summation alongside Kahan
113  return (sum0 + sum1) + (sum2 + sum3);
114  }
115  return 0.0;
116  }
117 };
118 
119 } // end namespace sc
120 
121 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const double * mpArray
bool hasSSE2()
KahanSum executeUnrolled(size_t &i, const double *pCurrent) const
constexpr double get() const
Returns the final sum.
Definition: kahan.hxx:190
This class provides LO with Kahan summation algorithm About this algorithm: https://en.wikipedia.org/wiki/Kahan_summation_algorithm For general purpose software we assume first order error is enough.
Definition: kahan.hxx:21
int i
ArraySumFunctor(const double *pArray, size_t nSize)
double executeSSE2(size_t &i, const double *pCurrent) const
if(aStr!=aBuf) UpdateName_Impl(m_xFollowLb.get()