5

I was playing around with some toy examples of floating point rounding errors in Ruby, and I noticed the following behaviour which surprised me.

First, an unsurprising example, where the rounding error occurs:

numbers = Array.new(10, 0.1)
#=> [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]

numbers.inject(0, :+)
#=> 0.9999999999999999

Now try the same with Enumerable#sum:

numbers.sum
#=> 1.0

The only thing I could find in the documentation that would hint at an explanation is

sum method may not respect method redefinition of “+” methods such as Integer#+.

so I suppose there's some kind of native code implementation in place to speed up things, but I would assume C floating points are also subject to IEEE-754 related imprecise arithmetic.

What is the reason for the behaviour in the second example? How is the sum method able to avoid the rounding error?

Erik Madsen
  • 1,913
  • 3
  • 20
  • 34
  • 8
    A comment in the [`sum` source](https://github.com/ruby/ruby/blob/4ea5c5610aeadecf78fdd2b7d6faad8574953620/enum.c#L3898) points to [A Generalized Kahan-Babuška-Summation-Algorithm](http://link.springer.com/article/10.1007/s00607-005-0139-x), which explains why `sum` is more accurate. – Amadan Aug 07 '19 at 12:39
  • That must be the answer. Thank you! Trying to search for the term led me to https://en.wikipedia.org/wiki/Kahan_summation_algorithm which explains the general principle well. – Erik Madsen Aug 07 '19 at 12:48

1 Answers1

0

Amadan gave the answer in a comment.

For floating point values Enumerable#sum uses an algorithm that compensates for the accumulation of error as the summation progresses.

As mentioned in the comment, the source code links to this paper and Wikipedia has an article on a variation of the described algorithm known as the Kahan Summation Algorithm.

Erik Madsen
  • 1,913
  • 3
  • 20
  • 34