@@ -2516,6 +2516,49 @@ Without arguments, equivalent to locals().\n\
25162516With an argument, equivalent to object.__dict__." );
25172517
25182518
2519+ /* Improved Kahan–Babuška algorithm by Arnold Neumaier
2520+ Neumaier, A. (1974), Rundungsfehleranalyse einiger Verfahren
2521+ zur Summation endlicher Summen. Z. angew. Math. Mech.,
2522+ 54: 39-51. https://doi.org/10.1002/zamm.19740540106
2523+ https://en.wikipedia.org/wiki/Kahan_summation_algorithm#Further_enhancements
2524+ */
2525+
2526+ typedef struct {
2527+ double hi ; /* high-order bits for a running sum */
2528+ double lo ; /* a running compensation for lost low-order bits */
2529+ } CompensatedSum ;
2530+
2531+ static inline CompensatedSum
2532+ cs_from_double (double x )
2533+ {
2534+ return (CompensatedSum ) {x };
2535+ }
2536+
2537+ static inline CompensatedSum
2538+ cs_add (CompensatedSum total , double x )
2539+ {
2540+ double t = total .hi + x ;
2541+ if (fabs (total .hi ) >= fabs (x )) {
2542+ total .lo += (total .hi - t ) + x ;
2543+ }
2544+ else {
2545+ total .lo += (x - t ) + total .hi ;
2546+ }
2547+ return (CompensatedSum ) {t , total .lo };
2548+ }
2549+
2550+ static inline double
2551+ cs_to_double (CompensatedSum total )
2552+ {
2553+ /* Avoid losing the sign on a negative result,
2554+ and don't let adding the compensation convert
2555+ an infinite or overflowed sum to a NaN. */
2556+ if (total .lo && isfinite (total .lo )) {
2557+ return total .hi + total .lo ;
2558+ }
2559+ return total .hi ;
2560+ }
2561+
25192562/*[clinic input]
25202563sum as builtin_sum
25212564
@@ -2628,37 +2671,18 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
26282671 }
26292672
26302673 if (PyFloat_CheckExact (result )) {
2631- double f_result = PyFloat_AS_DOUBLE (result );
2632- double c = 0.0 ;
2674+ CompensatedSum re_sum = cs_from_double (PyFloat_AS_DOUBLE (result ));
26332675 Py_SETREF (result , NULL );
26342676 while (result == NULL ) {
26352677 item = PyIter_Next (iter );
26362678 if (item == NULL ) {
26372679 Py_DECREF (iter );
26382680 if (PyErr_Occurred ())
26392681 return NULL ;
2640- /* Avoid losing the sign on a negative result,
2641- and don't let adding the compensation convert
2642- an infinite or overflowed sum to a NaN. */
2643- if (c && isfinite (c )) {
2644- f_result += c ;
2645- }
2646- return PyFloat_FromDouble (f_result );
2682+ return PyFloat_FromDouble (cs_to_double (re_sum ));
26472683 }
26482684 if (PyFloat_CheckExact (item )) {
2649- // Improved Kahan–Babuška algorithm by Arnold Neumaier
2650- // Neumaier, A. (1974), Rundungsfehleranalyse einiger Verfahren
2651- // zur Summation endlicher Summen. Z. angew. Math. Mech.,
2652- // 54: 39-51. https://doi.org/10.1002/zamm.19740540106
2653- // https://en.wikipedia.org/wiki/Kahan_summation_algorithm#Further_enhancements
2654- double x = PyFloat_AS_DOUBLE (item );
2655- double t = f_result + x ;
2656- if (fabs (f_result ) >= fabs (x )) {
2657- c += (f_result - t ) + x ;
2658- } else {
2659- c += (x - t ) + f_result ;
2660- }
2661- f_result = t ;
2685+ re_sum = cs_add (re_sum , PyFloat_AS_DOUBLE (item ));
26622686 _Py_DECREF_SPECIALIZED (item , _PyFloat_ExactDealloc );
26632687 continue ;
26642688 }
@@ -2667,15 +2691,70 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
26672691 int overflow ;
26682692 value = PyLong_AsLongAndOverflow (item , & overflow );
26692693 if (!overflow ) {
2670- f_result += (double )value ;
2694+ re_sum .hi += (double )value ;
2695+ Py_DECREF (item );
2696+ continue ;
2697+ }
2698+ }
2699+ result = PyFloat_FromDouble (cs_to_double (re_sum ));
2700+ if (result == NULL ) {
2701+ Py_DECREF (item );
2702+ Py_DECREF (iter );
2703+ return NULL ;
2704+ }
2705+ temp = PyNumber_Add (result , item );
2706+ Py_DECREF (result );
2707+ Py_DECREF (item );
2708+ result = temp ;
2709+ if (result == NULL ) {
2710+ Py_DECREF (iter );
2711+ return NULL ;
2712+ }
2713+ }
2714+ }
2715+
2716+ if (PyComplex_CheckExact (result )) {
2717+ Py_complex z = PyComplex_AsCComplex (result );
2718+ CompensatedSum re_sum = cs_from_double (z .real );
2719+ CompensatedSum im_sum = cs_from_double (z .imag );
2720+ Py_SETREF (result , NULL );
2721+ while (result == NULL ) {
2722+ item = PyIter_Next (iter );
2723+ if (item == NULL ) {
2724+ Py_DECREF (iter );
2725+ if (PyErr_Occurred ()) {
2726+ return NULL ;
2727+ }
2728+ return PyComplex_FromDoubles (cs_to_double (re_sum ),
2729+ cs_to_double (im_sum ));
2730+ }
2731+ if (PyComplex_CheckExact (item )) {
2732+ z = PyComplex_AsCComplex (item );
2733+ re_sum = cs_add (re_sum , z .real );
2734+ im_sum = cs_add (im_sum , z .imag );
2735+ Py_DECREF (item );
2736+ continue ;
2737+ }
2738+ 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 ;
2744+ im_sum .hi += 0.0 ;
26712745 Py_DECREF (item );
26722746 continue ;
26732747 }
26742748 }
2675- if (c && isfinite (c )) {
2676- f_result += c ;
2749+ if (PyFloat_Check (item )) {
2750+ double value = PyFloat_AS_DOUBLE (item );
2751+ re_sum .hi += value ;
2752+ im_sum .hi += 0.0 ;
2753+ _Py_DECREF_SPECIALIZED (item , _PyFloat_ExactDealloc );
2754+ continue ;
26772755 }
2678- result = PyFloat_FromDouble (f_result );
2756+ result = PyComplex_FromDoubles (cs_to_double (re_sum ),
2757+ cs_to_double (im_sum ));
26792758 if (result == NULL ) {
26802759 Py_DECREF (item );
26812760 Py_DECREF (iter );
0 commit comments