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. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy