@@ -2516,6 +2516,49 @@ 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 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
+
2519
2562
/*[clinic input]
2520
2563
sum as builtin_sum
2521
2564
@@ -2628,37 +2671,18 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
2628
2671
}
2629
2672
2630
2673
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 ));
2633
2675
Py_SETREF (result , NULL );
2634
2676
while (result == NULL ) {
2635
2677
item = PyIter_Next (iter );
2636
2678
if (item == NULL ) {
2637
2679
Py_DECREF (iter );
2638
2680
if (PyErr_Occurred ())
2639
2681
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 ));
2647
2683
}
2648
2684
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 ));
2662
2686
_Py_DECREF_SPECIALIZED (item , _PyFloat_ExactDealloc );
2663
2687
continue ;
2664
2688
}
@@ -2667,15 +2691,70 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
2667
2691
int overflow ;
2668
2692
value = PyLong_AsLongAndOverflow (item , & overflow );
2669
2693
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 ;
2671
2745
Py_DECREF (item );
2672
2746
continue ;
2673
2747
}
2674
2748
}
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 ;
2677
2755
}
2678
- result = PyFloat_FromDouble (f_result );
2756
+ result = PyComplex_FromDoubles (cs_to_double (re_sum ),
2757
+ cs_to_double (im_sum ));
2679
2758
if (result == NULL ) {
2680
2759
Py_DECREF (item );
2681
2760
Py_DECREF (iter );
0 commit comments