From e69b14a3b2b911d6469a87c0be4ce4f64c296829 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 28 Jun 2024 03:35:22 +0300 Subject: [PATCH 1/9] gh-121149: improve accuracy of builtin sum() for complex inputs --- Doc/library/functions.rst | 3 + Lib/test/test_builtin.py | 4 + ...-06-30-03-48-10.gh-issue-121149.lLBMKe.rst | 2 + Python/bltinmodule.c | 124 ++++++++++++++---- 4 files changed, 108 insertions(+), 25 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-06-30-03-48-10.gh-issue-121149.lLBMKe.rst diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 1d82f92ea67857..22e5830b797574 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1934,6 +1934,9 @@ are always available. They are listed here in alphabetical order. .. versionchanged:: 3.12 Summation of floats switched to an algorithm that gives higher accuracy and better commutativity on most builds. + .. versionchanged:: 3.14 Added specialization for summation of complexes, + using same algorithm as for summation of floats. + .. class:: super() super(type, object_or_type=None) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 9ff0f488dc4fa9..3c3d231bdc7174 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1775,6 +1775,10 @@ def __getitem__(self, index): def test_sum_accuracy(self): self.assertEqual(sum([0.1] * 10), 1.0) self.assertEqual(sum([1.0, 10E100, 1.0, -10E100]), 2.0) + 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([0.1j]*10 + [fractions.Fraction(1, 10)]), 0.1+1j) def test_type(self): self.assertEqual(type(''), type('123')) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-06-30-03-48-10.gh-issue-121149.lLBMKe.rst b/Misc/NEWS.d/next/Core and Builtins/2024-06-30-03-48-10.gh-issue-121149.lLBMKe.rst new file mode 100644 index 00000000000000..38d618f06090fd --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-06-30-03-48-10.gh-issue-121149.lLBMKe.rst @@ -0,0 +1,2 @@ +Added specialization for summation of complexes, this also improves accuracy +of builtin :func:`sum` for such inputs. Patch by Sergey B Kirpichev. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 6e50623cafa4ed..044b7c6a24e922 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2516,6 +2516,41 @@ 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 sum; /* accumulator */ + double c; /* a running compensation for lost low-order bits */ +} _csum; + +static inline void +_csum_neumaier_step(_csum *v, double x) +{ + double t = v->sum + x; + if (fabs(v->sum) >= fabs(x)) { + v->c += (v->sum - t) + x; + } else { + v->c += (x - t) + v->sum; + } + v->sum = t; +} + +static inline void +_csum_neumaier_finalize(_csum *v) +{ + /* 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 (v->c && isfinite(v->c)) { + v->sum += v->c; + } +} + /*[clinic input] sum as builtin_sum @@ -2628,8 +2663,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; + _csum f_result = {PyFloat_AS_DOUBLE(result), 0.0}; Py_SETREF(result, NULL); while(result == NULL) { item = PyIter_Next(iter); @@ -2637,28 +2671,61 @@ 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); + _csum_neumaier_finalize(&f_result); + return PyFloat_FromDouble(f_result.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; + _csum_neumaier_step(&f_result, PyFloat_AS_DOUBLE(item)); + _Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc); + continue; + } + if (PyLong_Check(item)) { + long value; + int overflow; + value = PyLong_AsLongAndOverflow(item, &overflow); + if (!overflow) { + f_result.sum += (double)value; + Py_DECREF(item); + continue; } - f_result = t; + } + _csum_neumaier_finalize(&f_result); + result = PyFloat_FromDouble(f_result.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); + _csum cr_result = {z.real, 0.0}; + _csum ci_result = {z.imag, 0.0}; + Py_SETREF(result, NULL); + while(result == NULL) { + item = PyIter_Next(iter); + if (item == NULL) { + Py_DECREF(iter); + if (PyErr_Occurred()) + return NULL; + _csum_neumaier_finalize(&cr_result); + _csum_neumaier_finalize(&ci_result); + return PyComplex_FromDoubles(cr_result.sum, ci_result.sum); + } + if (PyComplex_CheckExact(item)) { + z = PyComplex_AsCComplex(item); + _csum_neumaier_step(&cr_result, z.real); + _csum_neumaier_step(&ci_result, z.imag); _Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc); continue; } @@ -2667,15 +2734,22 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) int overflow; value = PyLong_AsLongAndOverflow(item, &overflow); if (!overflow) { - f_result += (double)value; + cr_result.sum += (double)value; + ci_result.sum += 0.0; Py_DECREF(item); continue; } } - if (c && isfinite(c)) { - f_result += c; + if (PyFloat_Check(item)) { + double value = PyFloat_AS_DOUBLE(item); + cr_result.sum += value; + ci_result.sum += 0.0; + Py_DECREF(item); + continue; } - result = PyFloat_FromDouble(f_result); + _csum_neumaier_finalize(&cr_result); + _csum_neumaier_finalize(&ci_result); + result = PyComplex_FromDoubles(cr_result.sum, ci_result.sum); if (result == NULL) { Py_DECREF(item); Py_DECREF(iter); From c43098063c6f0d02fccb048c94cd0aa99b64c1dc Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sun, 30 Jun 2024 14:12:04 +0300 Subject: [PATCH 2/9] + PEP7fy --- Python/bltinmodule.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 044b7c6a24e922..df6e45b0d3372a 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2525,7 +2525,7 @@ With an argument, equivalent to object.__dict__."); typedef struct { double sum; /* accumulator */ - double c; /* a running compensation for lost low-order bits */ + double c; /* a running compensation for lost low-order bits */ } _csum; static inline void @@ -2534,7 +2534,8 @@ _csum_neumaier_step(_csum *v, double x) double t = v->sum + x; if (fabs(v->sum) >= fabs(x)) { v->c += (v->sum - t) + x; - } else { + } + else { v->c += (x - t) + v->sum; } v->sum = t; @@ -2712,12 +2713,13 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) _csum cr_result = {z.real, 0.0}; _csum ci_result = {z.imag, 0.0}; Py_SETREF(result, NULL); - while(result == NULL) { + while (result == NULL) { item = PyIter_Next(iter); if (item == NULL) { Py_DECREF(iter); - if (PyErr_Occurred()) + if (PyErr_Occurred()) { return NULL; + } _csum_neumaier_finalize(&cr_result); _csum_neumaier_finalize(&ci_result); return PyComplex_FromDoubles(cr_result.sum, ci_result.sum); From 9998010b75e30367b28a0873c7daf3a897de3a86 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 2 Jul 2024 04:11:34 +0300 Subject: [PATCH 3/9] Fix _Py_DECREF_SPECIALIZED in PyComplex* branch --- Python/bltinmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index df6e45b0d3372a..9951fb6ea758da 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2728,7 +2728,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) z = PyComplex_AsCComplex(item); _csum_neumaier_step(&cr_result, z.real); _csum_neumaier_step(&ci_result, z.imag); - _Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc); + Py_DECREF(item); continue; } if (PyLong_Check(item)) { From 4c6f4f4eb2eefb3e0bc5e0095231efe99e077f11 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 2 Jul 2024 04:21:24 +0300 Subject: [PATCH 4/9] +1 --- Python/bltinmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 9951fb6ea758da..09a2c942db3098 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2746,7 +2746,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) double value = PyFloat_AS_DOUBLE(item); cr_result.sum += value; ci_result.sum += 0.0; - Py_DECREF(item); + _Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc); continue; } _csum_neumaier_finalize(&cr_result); From 2698be63ef7db50dcb0ba0868e7d5cf056dd82a5 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 3 Jul 2024 04:34:24 +0300 Subject: [PATCH 5/9] address review: better naming, some cleanup --- Python/bltinmodule.c | 65 +++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 09a2c942db3098..2854f23a70fe9e 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2524,32 +2524,33 @@ With an argument, equivalent to object.__dict__."); */ typedef struct { - double sum; /* accumulator */ - double c; /* a running compensation for lost low-order bits */ -} _csum; + double hi; /* high-order bits for a running sum */ + double lo; /* a running compensation for lost low-order bits */ +} CompensatedSum; static inline void -_csum_neumaier_step(_csum *v, double x) +cs_add(CompensatedSum *v, double x) { - double t = v->sum + x; - if (fabs(v->sum) >= fabs(x)) { - v->c += (v->sum - t) + x; + double t = v->hi + x; + if (fabs(v->hi) >= fabs(x)) { + v->lo += (v->hi - t) + x; } else { - v->c += (x - t) + v->sum; + v->lo += (x - t) + v->hi; } - v->sum = t; + v->hi = t; } -static inline void -_csum_neumaier_finalize(_csum *v) +static inline double +cs_to_double(CompensatedSum *v) { /* 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 (v->c && isfinite(v->c)) { - v->sum += v->c; + if (v->lo && isfinite(v->lo)) { + v->hi += v->lo; } + return v->hi; } /*[clinic input] @@ -2664,7 +2665,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) } if (PyFloat_CheckExact(result)) { - _csum f_result = {PyFloat_AS_DOUBLE(result), 0.0}; + CompensatedSum re_sum = {PyFloat_AS_DOUBLE(result)}; Py_SETREF(result, NULL); while(result == NULL) { item = PyIter_Next(iter); @@ -2672,11 +2673,10 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) Py_DECREF(iter); if (PyErr_Occurred()) return NULL; - _csum_neumaier_finalize(&f_result); - return PyFloat_FromDouble(f_result.sum); + return PyFloat_FromDouble(cs_to_double(&re_sum)); } if (PyFloat_CheckExact(item)) { - _csum_neumaier_step(&f_result, PyFloat_AS_DOUBLE(item)); + cs_add(&re_sum, PyFloat_AS_DOUBLE(item)); _Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc); continue; } @@ -2685,13 +2685,12 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) int overflow; value = PyLong_AsLongAndOverflow(item, &overflow); if (!overflow) { - f_result.sum += (double)value; + re_sum.hi += (double)value; Py_DECREF(item); continue; } } - _csum_neumaier_finalize(&f_result); - result = PyFloat_FromDouble(f_result.sum); + result = PyFloat_FromDouble(cs_to_double(&re_sum)); if (result == NULL) { Py_DECREF(item); Py_DECREF(iter); @@ -2710,8 +2709,8 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) if (PyComplex_CheckExact(result)) { Py_complex z = PyComplex_AsCComplex(result); - _csum cr_result = {z.real, 0.0}; - _csum ci_result = {z.imag, 0.0}; + CompensatedSum re_sum = {z.real}; + CompensatedSum im_sum = {z.imag}; Py_SETREF(result, NULL); while (result == NULL) { item = PyIter_Next(iter); @@ -2720,14 +2719,13 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) if (PyErr_Occurred()) { return NULL; } - _csum_neumaier_finalize(&cr_result); - _csum_neumaier_finalize(&ci_result); - return PyComplex_FromDoubles(cr_result.sum, ci_result.sum); + return PyComplex_FromDoubles(cs_to_double(&re_sum), + cs_to_double(&im_sum)); } if (PyComplex_CheckExact(item)) { z = PyComplex_AsCComplex(item); - _csum_neumaier_step(&cr_result, z.real); - _csum_neumaier_step(&ci_result, z.imag); + cs_add(&re_sum, z.real); + cs_add(&im_sum, z.imag); Py_DECREF(item); continue; } @@ -2736,22 +2734,21 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) int overflow; value = PyLong_AsLongAndOverflow(item, &overflow); if (!overflow) { - cr_result.sum += (double)value; - ci_result.sum += 0.0; + re_sum.hi += (double)value; + im_sum.hi += 0.0; Py_DECREF(item); continue; } } if (PyFloat_Check(item)) { double value = PyFloat_AS_DOUBLE(item); - cr_result.sum += value; - ci_result.sum += 0.0; + re_sum.hi += value; + im_sum.hi += 0.0; _Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc); continue; } - _csum_neumaier_finalize(&cr_result); - _csum_neumaier_finalize(&ci_result); - result = PyComplex_FromDoubles(cr_result.sum, ci_result.sum); + result = PyComplex_FromDoubles(cs_to_double(&re_sum), + cs_to_double(&im_sum)); if (result == NULL) { Py_DECREF(item); Py_DECREF(iter); From 5242bd67157ccd4ea301a8bf69811531fd0a5e8b Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 3 Jul 2024 05:04:26 +0300 Subject: [PATCH 6/9] address review: functional style --- Python/bltinmodule.c | 52 ++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 2854f23a70fe9e..a5b45e358d9efb 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2528,29 +2528,35 @@ typedef struct { double lo; /* a running compensation for lost low-order bits */ } CompensatedSum; -static inline void -cs_add(CompensatedSum *v, double x) +static inline CompensatedSum +cs_from_double(double x) { - double t = v->hi + x; - if (fabs(v->hi) >= fabs(x)) { - v->lo += (v->hi - t) + 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 { - v->lo += (x - t) + v->hi; + total.lo += (x - t) + total.hi; } - v->hi = t; + return (CompensatedSum) {t, total.lo}; } static inline double -cs_to_double(CompensatedSum *v) +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 (v->lo && isfinite(v->lo)) { - v->hi += v->lo; + if (total.lo && isfinite(total.lo)) { + return total.hi + total.lo; } - return v->hi; + return total.hi; } /*[clinic input] @@ -2665,7 +2671,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) } if (PyFloat_CheckExact(result)) { - CompensatedSum re_sum = {PyFloat_AS_DOUBLE(result)}; + CompensatedSum re_sum = cs_from_double(PyFloat_AS_DOUBLE(result)); Py_SETREF(result, NULL); while(result == NULL) { item = PyIter_Next(iter); @@ -2673,10 +2679,10 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) Py_DECREF(iter); if (PyErr_Occurred()) return NULL; - return PyFloat_FromDouble(cs_to_double(&re_sum)); + return PyFloat_FromDouble(cs_to_double(re_sum)); } if (PyFloat_CheckExact(item)) { - cs_add(&re_sum, PyFloat_AS_DOUBLE(item)); + re_sum = cs_add(re_sum, PyFloat_AS_DOUBLE(item)); _Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc); continue; } @@ -2690,7 +2696,7 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) continue; } } - result = PyFloat_FromDouble(cs_to_double(&re_sum)); + result = PyFloat_FromDouble(cs_to_double(re_sum)); if (result == NULL) { Py_DECREF(item); Py_DECREF(iter); @@ -2709,8 +2715,8 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) if (PyComplex_CheckExact(result)) { Py_complex z = PyComplex_AsCComplex(result); - CompensatedSum re_sum = {z.real}; - CompensatedSum im_sum = {z.imag}; + 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); @@ -2719,13 +2725,13 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) if (PyErr_Occurred()) { return NULL; } - return PyComplex_FromDoubles(cs_to_double(&re_sum), - cs_to_double(&im_sum)); + return PyComplex_FromDoubles(cs_to_double(re_sum), + cs_to_double(im_sum)); } if (PyComplex_CheckExact(item)) { z = PyComplex_AsCComplex(item); - cs_add(&re_sum, z.real); - cs_add(&im_sum, z.imag); + re_sum = cs_add(re_sum, z.real); + im_sum = cs_add(im_sum, z.imag); Py_DECREF(item); continue; } @@ -2747,8 +2753,8 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start) _Py_DECREF_SPECIALIZED(item, _PyFloat_ExactDealloc); continue; } - result = PyComplex_FromDoubles(cs_to_double(&re_sum), - cs_to_double(&im_sum)); + result = PyComplex_FromDoubles(cs_to_double(re_sum), + cs_to_double(im_sum)); if (result == NULL) { Py_DECREF(item); Py_DECREF(iter); From 1cec4abd4c93b700b2f71d3bf3d433d8bd99ca22 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 3 Jul 2024 11:52:34 +0300 Subject: [PATCH 7/9] + test invariant --- Lib/test/test_builtin.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 3c3d231bdc7174..3c70754f412944 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1768,6 +1768,11 @@ def __getitem__(self, index): sum(([x] for x in range(10)), empty) self.assertEqual(empty, []) + xs = [complex(random.random() - .5, random.random() - .5) + for _ in range(10000)] + self.assertEqual(sum(xs), complex(sum(_.real for _ in xs), + sum(_.imag for _ in xs))) + @requires_IEEE_754 @unittest.skipIf(HAVE_DOUBLE_ROUNDING, "sum accuracy not guaranteed on machines with double rounding") From 606b524c3d25df2c1874294d2f2d93274004727e Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 3 Jul 2024 10:08:45 -0500 Subject: [PATCH 8/9] Update Lib/test/test_builtin.py Co-authored-by: Sergey B Kirpichev --- Lib/test/test_builtin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 3c70754f412944..5818e96d61f480 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1770,8 +1770,8 @@ def __getitem__(self, index): xs = [complex(random.random() - .5, random.random() - .5) for _ in range(10000)] - self.assertEqual(sum(xs), complex(sum(_.real for _ in xs), - sum(_.imag for _ in xs))) + self.assertEqual(sum(xs), complex(sum(z.real for z in xs), + sum(z.imag for z in xs))) @requires_IEEE_754 @unittest.skipIf(HAVE_DOUBLE_ROUNDING, From 35cf4d8ca5b98faa305e90bacec9b3450b863afa Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 3 Jul 2024 10:10:16 -0500 Subject: [PATCH 9/9] Update Doc/library/functions.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/library/functions.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 22e5830b797574..17348dd907bf67 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -1934,7 +1934,8 @@ are always available. They are listed here in alphabetical order. .. versionchanged:: 3.12 Summation of floats switched to an algorithm that gives higher accuracy and better commutativity on most builds. - .. versionchanged:: 3.14 Added specialization for summation of complexes, + .. versionchanged:: 3.14 + Added specialization for summation of complexes, using same algorithm as for summation of floats.