summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorRaymond Hettinger <rhettinger@users.noreply.github.com>2022-12-23 22:35:58 (GMT)
committerGitHub <noreply@github.com>2022-12-23 22:35:58 (GMT)
commit5d84966cce6c596da22922a07f49bde959ff5201 (patch)
treee44414a0ff5f02233158709844e0932b08f54c63 /Python
parent1ecfd1ebf1f53ef6ac82085b25ed09952b470d4e (diff)
downloadcpython-5d84966cce6c596da22922a07f49bde959ff5201.zip
cpython-5d84966cce6c596da22922a07f49bde959ff5201.tar.gz
cpython-5d84966cce6c596da22922a07f49bde959ff5201.tar.bz2
GH-100425: Improve accuracy of builtin sum() for float inputs (GH-100426)
Diffstat (limited to 'Python')
-rw-r--r--Python/bltinmodule.c21
1 files changed, 20 insertions, 1 deletions
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index ff96c25..2d4822e 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -2532,6 +2532,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;
Py_SETREF(result, NULL);
while(result == NULL) {
item = PyIter_Next(iter);
@@ -2539,10 +2540,25 @@ 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 && Py_IS_FINITE(c)) {
+ f_result += c;
+ }
return PyFloat_FromDouble(f_result);
}
if (PyFloat_CheckExact(item)) {
- f_result += PyFloat_AS_DOUBLE(item);
+ // Improved Kahan–Babuška algorithm by Arnold Neumaier
+ // https://www.mat.univie.ac.at/~neum/scan/01.pdf
+ 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;
_Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc);
continue;
}
@@ -2556,6 +2572,9 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
continue;
}
}
+ if (c && Py_IS_FINITE(c)) {
+ f_result += c;
+ }
result = PyFloat_FromDouble(f_result);
if (result == NULL) {
Py_DECREF(item);