Kahan Summation Algorithm
Last Updated :
25 Mar, 2023
Prerequisites: Rounding off errors, Introduction to the floating-point representation
Kahan summation algorithm, also known as compensated summation and summation with the carry algorithm, is used to minimize the loss of significance in the total result obtained by adding a sequence of finite-precision floating-point numbers. This is done by keeping a separate running compensation (a variable to accumulate small errors).
Reason for Loss of Significance:
- As we know in Java language we have two primitive floating-point types, float and double, with single-precision 32-bit and double-precision 64-bit format values and operations specified by IEEE 754. That is, they are represented in a form like:
SIGN FRACTION * 2EXP
- For example, 0.15625 = (0.00101)2, which in floating-point format is represented as: 1.01 * 2-3. However, not all fractions can be represented exactly as a fraction of a power of two. For example, 0.1 = (0.000110011001100110011001100110011001100110011001100110011001… )2 and thus cannot be stored inside a floating-point variable.
- Therefore, floating-point error/loss of significance refers to when a number that cannot be stored as it is in the IEEE floating-point representation and repetitively some arithmetic operation is performed on it. This leads to some unexpected value and the difference between the expected and the obtained value is the error.
Below is an implementation that simulates the significance error:
C++
#include<bits/stdc++.h>
using namespace std;
double floatError( double no)
{
double sum = 0.0;
for ( int i = 0; i < 10; i++)
{
sum = sum + no;
}
return sum;
}
int main()
{
cout << setprecision(16);
cout << floatError(0.1);
}
|
Java
public class GFG {
public static double floatError( double no)
{
double sum = 0.0 ;
for ( int i = 0 ; i < 10 ; i++) {
sum = sum + no;
}
return sum;
}
public static void main(String[] args)
{
System.out.println(floatError( 0.1 ));
}
}
|
Python3
def floatError(no):
sum = 0.0
for i in range ( 10 ):
sum = sum + no
return sum
if __name__ = = '__main__' :
print (floatError( 0.1 ))
|
C#
using System;
public class Program
{
public static double FloatError( double no)
{
double sum = 0.0;
for ( int i = 0; i < 10; i++)
{
sum += no;
}
return Math.Round(sum - 1E-15, 15);
}
public static void Main()
{
Console.WriteLine($ "{FloatError(0.1):F15}" );
}
}
|
Javascript
<script>
function floatError(no)
{
let sum = 0.0;
for (let i = 0; i < 10; i++) {
sum = sum + no;
}
return sum;
}
document.write(floatError(0.1));
</script>
|
Output
0.9999999999999999
Note: The expected value for the above implementation is 1.0 but the value returned is 0.9999999999999999. Therefore, in this article, a method to reduce this error by using Kahan’s Summation algorithm is discussed.
Kahan Summation Algorithm: The idea of the Kahan summation algorithm is to compensate for the floating-point error by keeping a separate variable to store the running time errors as the arithmetic operations are being performed. This can be visualised by the following pseudocode:
function KahanSum(input)
var sum = 0.0
var c = 0.0
for i = 1 to input.length do
var y = input[i] - c
var t = sum + y
c = (t - sum) - y
sum = t
next i
return sum
In the above pseudocode, algebraically, the variable c in which the error is stored is always 0. However, when there is a loss of significance, it stores the error in it.
Below is the implementation of the above approach:
C++
#include <bits/stdc++.h>
using namespace std;
double kahanSum(vector< double > &fa)
{
double sum = 0.0;
double c = 0.0;
for ( double f : fa)
{
double y = f - c;
double t = sum + y;
c = (t - sum) - y;
sum = t;
}
return sum;
}
double sum(vector< double > &fa)
{
double sum = 0.0;
for ( double f : fa)
{
sum = sum + f;
}
return sum;
}
int main()
{
vector< double > no(10);
for ( int i = 0; i < 10; i++)
{
no[i] = 0.1;
}
cout << setprecision(16);
cout << "Normal sum: " << sum(no) << " \n" ;
cout << "Kahan sum: " << kahanSum(no);
}
|
Java
public class GFG {
private static double kahanSum( double ... fa)
{
double sum = 0.0 ;
double c = 0.0 ;
for ( double f : fa) {
double y = f - c;
double t = sum + y;
c = (t - sum) - y;
sum = t;
}
return sum;
}
private static double sum( double ... fa)
{
double sum = 0.0 ;
for ( double f : fa) {
sum = sum + f;
}
return sum;
}
public static void main(String[] args)
{
double [] no = new double [ 10 ];
for ( int i = 0 ; i < no.length; i++) {
no[i] = 0.1 ;
}
System.out.println( "Normal sum: " + sum(no));
System.out.println( "Kahan sum: " + kahanSum(no));
}
}
|
Javascript
<script>
function kahanSum(fa)
{
let sum = 0.0;
let c = 0.0;
for (let f = 0; f < fa.length; f++)
{
let y = fa[f] - c;
let t = sum + y;
c = (t - sum) - y;
sum = t;
}
return sum;
}
function sum(fa)
{
let sum = 0.0;
for (let f = 0; f < fa.length; f++)
{
sum = sum + fa[f];
}
return sum;
}
let no = new Array(10);
for (let i = 0; i < no.length; i++)
{
no[i] = 0.1;
}
document.write( "Normal sum: " +
sum(no) + "<br>" );
document.write( "Kahan sum: " +
kahanSum(no).toFixed(1) + "<br>" );
</script>
|
Python3
def kahanSum(fa):
sum = 0.0
c = 0.0
for f in fa:
y = f - c
t = sum + y
c = (t - sum ) - y
sum = t
return sum
if __name__ = = "__main__" :
no = [ 0.0 ] * 10
for i in range ( 10 ):
no[i] = 0.1
print ( "Normal sum: " , sum (no))
print ( "Kahan sum: " , kahanSum(no))
|
C#
using System;
class Program {
static void Main( string [] args) {
double [] no = new double [10];
for ( int i = 0; i < 10; i++) {
no[i] = 0.1;
}
Console.WriteLine( "Normal sum: {0}" , Sum(no));
Console.WriteLine( "Kahan sum: {0}" , KahanSum(no));
}
static double Sum( double [] fa) {
double sum = 0.0;
foreach ( double f in fa) {
sum += f;
}
return sum;
}
static double KahanSum( double [] fa) {
double sum = 0.0;
double c = 0.0;
foreach ( double f in fa) {
double y = f - c;
double t = sum + y;
c = (t - sum) - y;
sum = t;
}
return sum;
}
}
|
Output:
Normal sum: 0.9999999999999999
Kahan sum: 1.0
Time Complexity: O(N), where N is the length of the list.
Auxiliary Space: O(1)
Like Article
Suggest improvement
Share your thoughts in the comments
Please Login to comment...