Skip to content

Compensated summation #402

Open
Open
@Beliavsky

Description

@Beliavsky

Rosetta Code has a section on Kahan summation, which "is a method of summing a series of numbers represented in a limited precision in a way that minimises the loss of precision in the result." I have extracted and slightly modified the sumc function from the code there. Here is an example of how it can produce a more accurate result than the intrinsic SUM. I suggest that compensated summation be added to stdlib.

module m
implicit none
private
public :: sumc,wp
integer, parameter :: wp = kind(1.0d0)
contains
function sumc(a) result(asum)	!add elements of the array, using limited precision.
real(kind=wp)   , intent(in) :: a(:)
real(kind=wp)                :: asum
real(kind=wp)                :: s,c,y,t	! assistants.
integer 	             :: i ! stepper
integer                      :: n
n = size(a)
if (n < 1) then
   asum = 0.0_wp
   return
end if
s = a(1)	! start with the first element.
c = 0.0_wp	! no previous omissions to carry forward.
do i = 2,n	! step through the remainder of the array.
   y = a(i) - c		! combine the next value with the compensation.
   t = s + y		! augment the sum, temporarily in t.
   c = (t - s) - y	! catch what part of y didn't get added to t.
   s = t                ! place the sum.
end do		        ! on to the next element.
asum = s	        ! c will no longer be zero.
end function sumc
end module m
program main
! 04/26/2021 11:39 AM driver for sumc
use m, only: sumc,wp
implicit none
integer, parameter :: n = 100000000
real(kind=wp)      :: x(n)
real(kind=wp), parameter :: xadd = 10.0_wp**15
call random_seed()
call random_number(x)
x = x + xadd
print*,([sumc(x),sum(x)] - n*xadd)/n
end program main

output:
0.50331647999999996 2207252.1444556802

If you use wp = kind(1.0) (single precision) in the code above, you can see the difference even with xadd = 0.0. The output of

module m
implicit none
private
public :: sumc,wp
integer, parameter :: wp = kind(1.0)
contains
function sumc(a) result(asum)	!add elements of the array, using limited precision.
! adapted from Rosetta Code https://rosettacode.org/wiki/Kahan_summation
real(kind=wp)   , intent(in) :: a(:)
real(kind=wp)                :: asum
real(kind=wp)                :: s,c,y,t	! assistants.
integer 	             :: i ! stepper
integer                      :: n
n = size(a)
if (n < 1) then
   asum = 0.0_wp
   return
end if
s = a(1)	! start with the first element.
c = 0.0_wp	! no previous omissions to carry forward.
do i = 2,n	! step through the remainder of the array.
   y = a(i) - c		! combine the next value with the compensation.
   t = s + y		! augment the sum, temporarily in t.
   c = (t - s) - y	! catch what part of y didn't get added to t.
   s = t                ! place the sum.
end do		        ! on to the next element.
asum = s	        ! c will no longer be zero.
end function sumc
end module m
!
program main
! 04/26/2021 11:39 AM driver for sumc
use m, only: sumc,wp
implicit none
integer, parameter :: n = 100000000
real(kind=wp)      :: x(n)
call random_seed()
call random_number(x)
print*,[sumc(x),sum(x)]/n
end program main

is
0.499940693 0.167772159

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic: algorithmssearching and sorting, merging, ...

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions