Kahan summation

Introduction

The Kahan summation algorithm, also known as compensated summation or carried summation, is an algorithm used to reduce the error when accumulating a sequence of floating-point values with finite precision. It works mainly by maintaining a separate variable to accumulate the error (commonly named cc).

William Kahan primarily developed the algorithm in the 1960s. Because Ivo Babuška independently proposed a similar algorithm, Kahan summation is also known as Kahan–Babuška summation.

Rounding error

In computer programs, real numbers must be represented approximately with a finite number of digits. Today, most computers use the floating-point representation specified by IEEE-754 for this purpose. For 13\frac{1}{3}, we cannot represent the value exactly with finitely many digits, so some value must be rounded (truncated) when IEEE-754 representation is used. This rounding error is a characteristic of floating-point computation.

Floating-point addition is commutative but not associative. In other words, a+b=b+aa+b = b+a, but (a+b)+c≠a+(b+c)(a+b)+c \neq a+(b+c). Thus, when adding a sequence of floating-point values, we may either accumulate them one by one from left to right or pair them according to their original order. The second method is relatively slower and requires more memory; it is also used by some specialized summation functions in programming languages, and generally produces more accurate results.

To obtain a more accurate floating-point sum, we use the Kahan summation algorithm.

When computing Snew=Sold+aS_{new}=S_{old}+a (where aa is one value in the floating-point sequence), define the value actually added to SS as aeff=Snew−Solda_{eff}=S_{new}-S_{old}. If aeffa_{eff} is greater than aa, there is an upward rounding error; if aeffa_{eff} is less than aa, there is a downward rounding error. Define the rounding error as Eroundoff=aeff−aE_{roundoff} = a_{eff} - a. The value used to correct this rounding error is a−aeffa-a_{eff}, namely the negative of EroundoffE_{roundoff}. If cc denotes the variable that compensates for lost low-order bits, we obtain cnew=cold+(a−aeff)c_{new} = c_{old} + (a - a_{eff}).

Process

The Kahan summation algorithm mainly uses a separate variable to accumulate error. In the reference code below, sumsum is the final accumulated result. cc compensates for lost low-order bits (the part that was discarded), and is essential to Kahan summation.

Because sumsum is large and yy is small, the low-order bits of yy are lost. (t−sum)(t - sum) cancels the high-order part of yy, and subtracting yy restores the negative value (the low-order part of yy). Therefore, in exact arithmetic, cc is always zero. In the next iteration, the lost low-order part is added to yy.

Implementation

Reference code
float kahanSum(vector<float> nums) {
  float sum = 0.0f;
  float c = 0.0f;
  for (auto num : nums) {
    float y = num - c;
    float t = sum + y;
    c = (t - sum) - y;
    sum = t;
  }
  return sum;
}

Exercises

In OI, Kahan summation mainly serves as an auxiliary tool that provides a value with a smaller numerical error.

Example CodeForces Contest 800 Problem A. Voltage Keepsake

There are nn devices operating simultaneously. Device ii consumes aia_{i} units of power per second, continuously; that is, over λ\lambda seconds it consumes λ×ai\lambda \times a_{i} units of power. Device ii currently stores bib_{i} units of energy, and every device can store any amount of energy. There is a charger that can be connected to any one device. It supplies pp units of energy per second, continuously; that is, a device connected for λ\lambda seconds receives λ×p\lambda \times p units of energy. We may switch the device being charged at any time, including a real-valued time, and switching takes no time. Find the longest time for which the devices can be used before one of them reaches 00 units of power.

Example CodeForces Contest 504 Problem B. Misha and Permutations Summation

For the numbers 0,1,⋯ ,(n−1)0, 1, \cdots, (n - 1), define the sum of two permutations pp and qq as Perm((Ord(p)+Ord(q)) mod n!)Perm((Ord(p)+Ord(q))\bmod n!), where Perm(x)Perm(x) is the 0,1,⋯ ,(n−1)0, 1, \cdots, (n-1)-th lexicographic permutation of the numbers xx, counted from zero, and Ord(p)Ord(p) is the lexicographic rank of permutation pp. For example, Perm(0)=(0,1,⋯ ,n−2,n−1)Perm(0) = (0, 1, \cdots , n - 2, n - 1) and Perm(n!−1)=(n−1,n−2,⋯ ,1,0))Perm(n! - 1) = (n - 1, n-2,\cdots, 1,0)). Given two permutations pp and qq, find their sum.

Summation in programming languages

Python's standard library provides the fsum function for accurately rounded floating-point summation of values in an iterable. It avoids loss of precision by tracking multiple partial sums using the Shewchuk algorithm.

In Julia, the default implementation of the sum function uses pairwise summation to obtain high precision and good performance. The external library function sum_kbn provides a Neumaier-variant implementation for cases requiring higher precision; see KahanSummation.jl for details.

References and notes

  1. Kahan_summation_algorithm - Wikipedia
  2. Kahan summation - Rosetta Code
  3. VK Cup Round 2 + Codeforces Round 409 Announcement
  4. Rounding off errors in Java - GeeksforGeeks

This content comes from OI Wiki: Kahan summation. See the original for authors and revision history. AIOJ has adapted the formatting, in-site links, and resource paths.

Content is shared under CC BY-SA 4.0 and SATA license; additional terms are governed by the original notice. Thanks to the OI Wiki authors and contributors; per the SATA terms, please visit the OI Wiki repository and star it to show your support.