From a45843282941a2a7f62826ab7a05e35cd1f29667 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 24 Jul 2024 17:12:00 +0300 Subject: [PATCH 1/3] gh-122234: fix accuracy issues for sum() * 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 | 3 +++ ...4-07-24-17-11-51.gh-issue-122234.VxsP_F.rst | 1 + Python/bltinmodule.c | 18 +++++++----------- 3 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-07-24-17-11-51.gh-issue-122234.VxsP_F.rst diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index c6a563cc90fec4..35ff53ed458271 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1803,6 +1803,9 @@ def test_sum_accuracy(self): 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 index 00000000000000..9b9f1046f07308 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-07-24-17-11-51.gh-issue-122234.VxsP_F.rst @@ -0,0 +1 @@ +Fixed complex-valued specialization for :func:`sum`. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 3f7bf4d568ee46..ef9e3048813290 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2687,11 +2687,9 @@ 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; } @@ -2736,11 +2734,9 @@ 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; @@ -2748,7 +2744,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) } 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; From 8fb951b713e30d30a9018d400e0ad8f42e298bb3 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 24 Jul 2024 19:29:29 +0300 Subject: [PATCH 2/3] + expand news --- .../2024-07-24-17-11-51.gh-issue-122234.VxsP_F.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 index 9b9f1046f07308..b86d6fbdfc648f 100644 --- 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 @@ -1 +1,4 @@ -Fixed complex-valued specialization for :func:`sum`. +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. From 46dcff7c6bcea828f8ce4a46118b8f1af809372b Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 24 Jul 2024 20:36:27 +0300 Subject: [PATCH 3/3] address review: quick exit on OverflowError --- Lib/test/test_builtin.py | 2 ++ Python/bltinmodule.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 35ff53ed458271..85f139db9bcd45 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1778,6 +1778,8 @@ def test_sum(self): 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): diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index ef9e3048813290..ae025e767ec838 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2693,6 +2693,9 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) Py_DECREF(item); continue; } + else { + return NULL; + } } result = PyFloat_FromDouble(cs_to_double(re_sum)); if (result == NULL) { @@ -2741,6 +2744,9 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) Py_DECREF(item); continue; } + else { + return NULL; + } } if (PyFloat_Check(item)) { double value = PyFloat_AS_DOUBLE(item);