summaryrefslogtreecommitdiffstats
path: root/Python/bltinmodule.c
diff options
context:
space:
mode:
authorSergey B Kirpichev <skirpichev@gmail.com>2024-07-05 15:01:05 (GMT)
committerGitHub <noreply@github.com>2024-07-05 15:01:05 (GMT)
commitd4faa7bd321a7016f3e987d65962e02c778d708f (patch)
treea8a04881c376cd9eed4e35462be574e26be87686 /Python/bltinmodule.c
parentcecd6012b0ed5dca3916ae341e705ae44172991d (diff)
downloadcpython-d4faa7bd321a7016f3e987d65962e02c778d708f.zip
cpython-d4faa7bd321a7016f3e987d65962e02c778d708f.tar.gz
cpython-d4faa7bd321a7016f3e987d65962e02c778d708f.tar.bz2
gh-121149: improve accuracy of builtin sum() for complex inputs (gh-121176)
Diffstat (limited to 'Python/bltinmodule.c')
-rw-r--r--Python/bltinmodule.c131
1 files changed, 105 insertions, 26 deletions
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 6e50623..a5b45e3 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -2516,6 +2516,49 @@ Without arguments, equivalent to locals().\n\
With an argument, equivalent to object.__dict__.");
+/* Improved Kahan–Babuška algorithm by Arnold Neumaier
+ Neumaier, A. (1974), Rundungsfehleranalyse einiger Verfahren
+ zur Summation endlicher Summen. Z. angew. Math. Mech.,
+ 54: 39-51. https://doi.org/10.1002/zamm.19740540106
+ https://en.wikipedia.org/wiki/Kahan_summation_algorithm#Further_enhancements
+ */
+
+typedef struct {
+ double hi; /* high-order bits for a running sum */
+ double lo; /* a running compensation for lost low-order bits */
+} CompensatedSum;
+
+static inline CompensatedSum
+cs_from_double(double x)
+{
+ return (CompensatedSum) {x};
+}
+
+static inline CompensatedSum
+cs_add(CompensatedSum total, double x)
+{
+ double t = total.hi + x;
+ if (fabs(total.hi) >= fabs(x)) {
+ total.lo += (total.hi - t) + x;
+ }
+ else {
+ total.lo += (x - t) + total.hi;
+ }
+ return (CompensatedSum) {t, total.lo};
+}
+
+static inline double
+cs_to_double(CompensatedSum total)
+{
+ /* Avoid losing the sign on a negative result,
+ and don't let adding the compensation convert
+ an infinite or overflowed sum to a NaN. */
+ if (total.lo && isfinite(total.lo)) {
+ return total.hi + total.lo;
+ }
+ return total.hi;
+}
+
/*[clinic input]
sum as builtin_sum
@@ -2628,8 +2671,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
}
if (PyFloat_CheckExact(result)) {
- double f_result = PyFloat_AS_DOUBLE(result);
- double c = 0.0;
+ CompensatedSum re_sum = cs_from_double(PyFloat_AS_DOUBLE(result));
Py_SETREF(result, NULL);
while(result == NULL) {
item = PyIter_Next(iter);
@@ -2637,28 +2679,10 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
Py_DECREF(iter);
if (PyErr_Occurred())
return NULL;
- /* Avoid losing the sign on a negative result,
- and don't let adding the compensation convert
- an infinite or overflowed sum to a NaN. */
- if (c && isfinite(c)) {
- f_result += c;
- }
- return PyFloat_FromDouble(f_result);
+ return PyFloat_FromDouble(cs_to_double(re_sum));
}
if (PyFloat_CheckExact(item)) {
- // Improved Kahan–Babuška algorithm by Arnold Neumaier
- // Neumaier, A. (1974), Rundungsfehleranalyse einiger Verfahren
- // zur Summation endlicher Summen. Z. angew. Math. Mech.,
- // 54: 39-51. https://doi.org/10.1002/zamm.19740540106
- // https://en.wikipedia.org/wiki/Kahan_summation_algorithm#Further_enhancements
- double x = PyFloat_AS_DOUBLE(item);
- double t = f_result + x;
- if (fabs(f_result) >= fabs(x)) {
- c += (f_result - t) + x;
- } else {
- c += (x - t) + f_result;
- }
- f_result = t;
+ re_sum = cs_add(re_sum, PyFloat_AS_DOUBLE(item));
_Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc);
continue;
}
@@ -2667,15 +2691,70 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
int overflow;
value = PyLong_AsLongAndOverflow(item, &overflow);
if (!overflow) {
- f_result += (double)value;
+ re_sum.hi += (double)value;
+ Py_DECREF(item);
+ continue;
+ }
+ }
+ result = PyFloat_FromDouble(cs_to_double(re_sum));
+ if (result == NULL) {
+ Py_DECREF(item);
+ Py_DECREF(iter);
+ return NULL;
+ }
+ temp = PyNumber_Add(result, item);
+ Py_DECREF(result);
+ Py_DECREF(item);
+ result = temp;
+ if (result == NULL) {
+ Py_DECREF(iter);
+ return NULL;
+ }
+ }
+ }
+
+ if (PyComplex_CheckExact(result)) {
+ Py_complex z = PyComplex_AsCComplex(result);
+ CompensatedSum re_sum = cs_from_double(z.real);
+ CompensatedSum im_sum = cs_from_double(z.imag);
+ Py_SETREF(result, NULL);
+ while (result == NULL) {
+ item = PyIter_Next(iter);
+ if (item == NULL) {
+ Py_DECREF(iter);
+ if (PyErr_Occurred()) {
+ return NULL;
+ }
+ return PyComplex_FromDoubles(cs_to_double(re_sum),
+ cs_to_double(im_sum));
+ }
+ if (PyComplex_CheckExact(item)) {
+ z = PyComplex_AsCComplex(item);
+ re_sum = cs_add(re_sum, z.real);
+ im_sum = cs_add(im_sum, z.imag);
+ Py_DECREF(item);
+ continue;
+ }
+ if (PyLong_Check(item)) {
+ long value;
+ int overflow;
+ value = PyLong_AsLongAndOverflow(item, &overflow);
+ if (!overflow) {
+ re_sum.hi += (double)value;
+ im_sum.hi += 0.0;
Py_DECREF(item);
continue;
}
}
- if (c && isfinite(c)) {
- f_result += c;
+ if (PyFloat_Check(item)) {
+ double value = PyFloat_AS_DOUBLE(item);
+ re_sum.hi += value;
+ im_sum.hi += 0.0;
+ _Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc);
+ continue;
}
- result = PyFloat_FromDouble(f_result);
+ result = PyComplex_FromDoubles(cs_to_double(re_sum),
+ cs_to_double(im_sum));
if (result == NULL) {
Py_DECREF(item);
Py_DECREF(iter);