@@ -2516,6 +2516,41 @@ Without arguments, equivalent to locals().\n\
2516
2516
With an argument, equivalent to object.__dict__." );
2517
2517
2518
2518
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 sum ; /* accumulator */
2528
+ double c ; /* a running compensation for lost low-order bits */
2529
+ } _csum ;
2530
+
2531
+ static inline void
2532
+ _csum_neumaier_step (_csum * v , double x )
2533
+ {
2534
+ double t = v -> sum + x ;
2535
+ if (fabs (v -> sum ) >= fabs (x )) {
2536
+ v -> c += (v -> sum - t ) + x ;
2537
+ } else {
2538
+ v -> c += (x - t ) + v -> sum ;
2539
+ }
2540
+ v -> sum = t ;
2541
+ }
2542
+
2543
+ static inline void
2544
+ _csum_neumaier_finalize (_csum * v )
2545
+ {
2546
+ /* Avoid losing the sign on a negative result,
2547
+ and don't let adding the compensation convert
2548
+ an infinite or overflowed sum to a NaN. */
2549
+ if (v -> c && isfinite (v -> c )) {
2550
+ v -> sum += v -> c ;
2551
+ }
2552
+ }
2553
+
2519
2554
/*[clinic input]
2520
2555
sum as builtin_sum
2521
2556
@@ -2628,37 +2663,69 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
2628
2663
}
2629
2664
2630
2665
if (PyFloat_CheckExact (result )) {
2631
- double f_result = PyFloat_AS_DOUBLE (result );
2632
- double c = 0.0 ;
2666
+ _csum f_result = {PyFloat_AS_DOUBLE (result ), 0.0 };
2633
2667
Py_SETREF (result , NULL );
2634
2668
while (result == NULL ) {
2635
2669
item = PyIter_Next (iter );
2636
2670
if (item == NULL ) {
2637
2671
Py_DECREF (iter );
2638
2672
if (PyErr_Occurred ())
2639
2673
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 );
2674
+ _csum_neumaier_finalize (& f_result );
2675
+ return PyFloat_FromDouble (f_result .sum );
2647
2676
}
2648
2677
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 ;
2678
+ _csum_neumaier_step (& f_result , PyFloat_AS_DOUBLE (item ));
2679
+ _Py_DECREF_SPECIALIZED (item , _PyFloat_ExactDealloc );
2680
+ continue ;
2681
+ }
2682
+ if (PyLong_Check (item )) {
2683
+ long value ;
2684
+ int overflow ;
2685
+ value = PyLong_AsLongAndOverflow (item , & overflow );
2686
+ if (!overflow ) {
2687
+ f_result .sum += (double )value ;
2688
+ Py_DECREF (item );
2689
+ continue ;
2660
2690
}
2661
- f_result = t ;
2691
+ }
2692
+ _csum_neumaier_finalize (& f_result );
2693
+ result = PyFloat_FromDouble (f_result .sum );
2694
+ if (result == NULL ) {
2695
+ Py_DECREF (item );
2696
+ Py_DECREF (iter );
2697
+ return NULL ;
2698
+ }
2699
+ temp = PyNumber_Add (result , item );
2700
+ Py_DECREF (result );
2701
+ Py_DECREF (item );
2702
+ result = temp ;
2703
+ if (result == NULL ) {
2704
+ Py_DECREF (iter );
2705
+ return NULL ;
2706
+ }
2707
+ }
2708
+ }
2709
+
2710
+ if (PyComplex_CheckExact (result )) {
2711
+ Py_complex z = PyComplex_AsCComplex (result );
2712
+ _csum cr_result = {z .real , 0.0 };
2713
+ _csum ci_result = {z .imag , 0.0 };
2714
+ Py_SETREF (result , NULL );
2715
+ while (result == NULL ) {
2716
+ item = PyIter_Next (iter );
2717
+ if (item == NULL ) {
2718
+ Py_DECREF (iter );
2719
+ if (PyErr_Occurred ())
2720
+ return NULL ;
2721
+ _csum_neumaier_finalize (& cr_result );
2722
+ _csum_neumaier_finalize (& ci_result );
2723
+ return PyComplex_FromDoubles (cr_result .sum , ci_result .sum );
2724
+ }
2725
+ if (PyComplex_CheckExact (item )) {
2726
+ z = PyComplex_AsCComplex (item );
2727
+ _csum_neumaier_step (& cr_result , z .real );
2728
+ _csum_neumaier_step (& ci_result , z .imag );
2662
2729
_Py_DECREF_SPECIALIZED (item , _PyFloat_ExactDealloc );
2663
2730
continue ;
2664
2731
}
@@ -2667,15 +2734,22 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
2667
2734
int overflow ;
2668
2735
value = PyLong_AsLongAndOverflow (item , & overflow );
2669
2736
if (!overflow ) {
2670
- f_result += (double )value ;
2737
+ cr_result .sum += (double )value ;
2738
+ ci_result .sum += 0.0 ;
2671
2739
Py_DECREF (item );
2672
2740
continue ;
2673
2741
}
2674
2742
}
2675
- if (c && isfinite (c )) {
2676
- f_result += c ;
2743
+ if (PyFloat_Check (item )) {
2744
+ double value = PyFloat_AS_DOUBLE (item );
2745
+ cr_result .sum += value ;
2746
+ ci_result .sum += 0.0 ;
2747
+ Py_DECREF (item );
2748
+ continue ;
2677
2749
}
2678
- result = PyFloat_FromDouble (f_result );
2750
+ _csum_neumaier_finalize (& cr_result );
2751
+ _csum_neumaier_finalize (& ci_result );
2752
+ result = PyComplex_FromDoubles (cr_result .sum , ci_result .sum );
2679
2753
if (result == NULL ) {
2680
2754
Py_DECREF (item );
2681
2755
Py_DECREF (iter );
0 commit comments