Skip to content

Commit 169e713

Browse files
authored
gh-122234: fix accuracy issues for sum() (#122236)
* Use compensated summation for complex sums with floating-point items. This amends #121176. * sum() specializations for floats and complexes now use PyLong_AsDouble() instead of PyLong_AsLongAndOverflow() and compensated summation as well.
1 parent bc93923 commit 169e713

File tree

3 files changed

+22
-11
lines changed

3 files changed

+22
-11
lines changed

Lib/test/test_builtin.py

+5
Original file line numberDiff line numberDiff line change
@@ -1778,6 +1778,8 @@ def test_sum(self):
17781778
self.assertRaises(TypeError, sum, [], '')
17791779
self.assertRaises(TypeError, sum, [], b'')
17801780
self.assertRaises(TypeError, sum, [], bytearray())
1781+
self.assertRaises(OverflowError, sum, [1.0, 10**1000])
1782+
self.assertRaises(OverflowError, sum, [1j, 10**1000])
17811783

17821784
class BadSeq:
17831785
def __getitem__(self, index):
@@ -1803,6 +1805,9 @@ def test_sum_accuracy(self):
18031805
self.assertEqual(sum([1.0, 10E100, 1.0, -10E100, 2j]), 2+2j)
18041806
self.assertEqual(sum([2+1j, 10E100j, 1j, -10E100j]), 2+2j)
18051807
self.assertEqual(sum([1j, 1, 10E100j, 1j, 1.0, -10E100j]), 2+2j)
1808+
self.assertEqual(sum([2j, 1., 10E100, 1., -10E100]), 2+2j)
1809+
self.assertEqual(sum([1.0, 10**100, 1.0, -10**100]), 2.0)
1810+
self.assertEqual(sum([2j, 1.0, 10**100, 1.0, -10**100]), 2+2j)
18061811
self.assertEqual(sum([0.1j]*10 + [fractions.Fraction(1, 10)]), 0.1+1j)
18071812

18081813
def test_type(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Specializations for sums with float and complex inputs in :func:`sum()` now
2+
always use compensated summation. Also, for integer items in above
3+
specializations: :c:func:`PyLong_AsDouble` is used, instead of
4+
:c:func:`PyLong_AsLongAndOverflow`. Patch by Sergey B Kirpichev.

Python/bltinmodule.c

+13-11
Original file line numberDiff line numberDiff line change
@@ -2687,14 +2687,15 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
26872687
continue;
26882688
}
26892689
if (PyLong_Check(item)) {
2690-
long value;
2691-
int overflow;
2692-
value = PyLong_AsLongAndOverflow(item, &overflow);
2693-
if (!overflow) {
2694-
re_sum.hi += (double)value;
2690+
double value = PyLong_AsDouble(item);
2691+
if (value != -1.0 || !PyErr_Occurred()) {
2692+
re_sum = cs_add(re_sum, value);
26952693
Py_DECREF(item);
26962694
continue;
26972695
}
2696+
else {
2697+
return NULL;
2698+
}
26982699
}
26992700
result = PyFloat_FromDouble(cs_to_double(re_sum));
27002701
if (result == NULL) {
@@ -2736,19 +2737,20 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
27362737
continue;
27372738
}
27382739
if (PyLong_Check(item)) {
2739-
long value;
2740-
int overflow;
2741-
value = PyLong_AsLongAndOverflow(item, &overflow);
2742-
if (!overflow) {
2743-
re_sum.hi += (double)value;
2740+
double value = PyLong_AsDouble(item);
2741+
if (value != -1.0 || !PyErr_Occurred()) {
2742+
re_sum = cs_add(re_sum, value);
27442743
im_sum.hi += 0.0;
27452744
Py_DECREF(item);
27462745
continue;
27472746
}
2747+
else {
2748+
return NULL;
2749+
}
27482750
}
27492751
if (PyFloat_Check(item)) {
27502752
double value = PyFloat_AS_DOUBLE(item);
2751-
re_sum.hi += value;
2753+
re_sum = cs_add(re_sum, value);
27522754
im_sum.hi += 0.0;
27532755
_Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc);
27542756
continue;

0 commit comments

Comments
 (0)