]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-122234: fix accuracy issues for sum() (#122236)
authorSergey B Kirpichev <skirpichev@gmail.com>
Mon, 29 Jul 2024 03:56:40 +0000 (06:56 +0300)
committerGitHub <noreply@github.com>
Mon, 29 Jul 2024 03:56:40 +0000 (05:56 +0200)
* 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.

Lib/test/test_builtin.py
Misc/NEWS.d/next/Core and Builtins/2024-07-24-17-11-51.gh-issue-122234.VxsP_F.rst [new file with mode: 0644]
Python/bltinmodule.c

index c6a563cc90fec47933f4f8f4f210b6a78fbf7371..85f139db9bcd45f0f7c7f1ecf210756686851b8a 100644 (file)
@@ -1778,6 +1778,8 @@ class BuiltinTest(unittest.TestCase):
         self.assertRaises(TypeError, sum, [], '')
         self.assertRaises(TypeError, sum, [], b'')
         self.assertRaises(TypeError, sum, [], bytearray())
+        self.assertRaises(OverflowError, sum, [1.0, 10**1000])
+        self.assertRaises(OverflowError, sum, [1j, 10**1000])
 
         class BadSeq:
             def __getitem__(self, index):
@@ -1803,6 +1805,9 @@ class BuiltinTest(unittest.TestCase):
         self.assertEqual(sum([1.0, 10E100, 1.0, -10E100, 2j]), 2+2j)
         self.assertEqual(sum([2+1j, 10E100j, 1j, -10E100j]), 2+2j)
         self.assertEqual(sum([1j, 1, 10E100j, 1j, 1.0, -10E100j]), 2+2j)
+        self.assertEqual(sum([2j, 1., 10E100, 1., -10E100]), 2+2j)
+        self.assertEqual(sum([1.0, 10**100, 1.0, -10**100]), 2.0)
+        self.assertEqual(sum([2j, 1.0, 10**100, 1.0, -10**100]), 2+2j)
         self.assertEqual(sum([0.1j]*10 + [fractions.Fraction(1, 10)]), 0.1+1j)
 
     def test_type(self):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-24-17-11-51.gh-issue-122234.VxsP_F.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-24-17-11-51.gh-issue-122234.VxsP_F.rst
new file mode 100644 (file)
index 0000000..b86d6fb
--- /dev/null
@@ -0,0 +1,4 @@
+Specializations for sums with float and complex inputs in :func:`sum()` now
+always use compensated summation.  Also, for integer items in above
+specializations: :c:func:`PyLong_AsDouble` is used, instead of
+:c:func:`PyLong_AsLongAndOverflow`.  Patch by Sergey B Kirpichev.
index 3f7bf4d568ee4639104d7fde6c4609b59486d20e..ae025e767ec838775b183685dfc9fc69b9893ede 100644 (file)
@@ -2687,14 +2687,15 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
                 continue;
             }
             if (PyLong_Check(item)) {
-                long value;
-                int overflow;
-                value = PyLong_AsLongAndOverflow(item, &overflow);
-                if (!overflow) {
-                    re_sum.hi += (double)value;
+                double value = PyLong_AsDouble(item);
+                if (value != -1.0 || !PyErr_Occurred()) {
+                    re_sum = cs_add(re_sum, value);
                     Py_DECREF(item);
                     continue;
                 }
+                else {
+                    return NULL;
+                }
             }
             result = PyFloat_FromDouble(cs_to_double(re_sum));
             if (result == NULL) {
@@ -2736,19 +2737,20 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
                 continue;
             }
             if (PyLong_Check(item)) {
-                long value;
-                int overflow;
-                value = PyLong_AsLongAndOverflow(item, &overflow);
-                if (!overflow) {
-                    re_sum.hi += (double)value;
+                double value = PyLong_AsDouble(item);
+                if (value != -1.0 || !PyErr_Occurred()) {
+                    re_sum = cs_add(re_sum, value);
                     im_sum.hi += 0.0;
                     Py_DECREF(item);
                     continue;
                 }
+                else {
+                    return NULL;
+                }
             }
             if (PyFloat_Check(item)) {
                 double value = PyFloat_AS_DOUBLE(item);
-                re_sum.hi += value;
+                re_sum = cs_add(re_sum, value);
                 im_sum.hi += 0.0;
                 _Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc);
                 continue;