Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ruby floats add up to different values depending on the order

So this is weird. I'm in Ruby 1.9.3, and float addition is not working as I expect it would.

0.3 + 0.6 + 0.1 = 0.9999999999999999
0.6 + 0.1 + 0.3 = 1

I've tried this on another machine and get the same result. Any idea why this might happen?

like image 614
Marc Avatar asked Dec 12 '12 20:12

Marc


3 Answers

Floating point operations are inexact: they round the result to nearest representable float value.
That means that each float operation is:

float(a op b) = mathematical(a op b) + rounding-error( a op b )

As suggested by above equation, the rounding error depends on operands a & b.
Thus, if you perform operations in different order,

float(float( a op b) op c) != float(a op (b op c))

In other words, floating point operations are not associative.
They are commutative though...

As other said, transforming a decimal representation 0.1 (that is 1/10) into a base 2 representation (that is 1/16 + 1/64 + ... ) would lead to an infinite serie of digits. So float(0.1) is not equal to 1/10 exactly, it also has a rounding-error and it leads to a long serie of binary digits, which explains that following operations have a non null rounding-error (mathematical result is not representable in floating point)

like image 84
aka.nice Avatar answered Nov 08 '22 12:11

aka.nice


First, the numerals “0.3”, “.6”, and “.1” in the source text are converted to floating-point numbers, which I will call a, b, and c. These values are near .3, .6, and .1 but not equal to them, but that is not directly the reason you see different results.

In each floating-point arithmetic operation, there may be a little rounding error, some small number ei. So the exact mathematical results your two expressions calculate is:

(a + b + e0) + c + e1 and (b + c + e2) + a + e3.

That is, in the first expression, a is added to b, and there is a slight rounding error e0. Then c is added, and there is a slight rounding error e1. In the second expression, b is added to c, and there is a slight rounding error e2. Finally, a is added, and there is a slight rounding error e3.

The reason your results differ is that e0 + e1e2 + e3. That is, the rounding that was necessary when a and b were added was different from the rounding that was necessary when b and c were added and/or the roundings that were necessary in the second additions of the two cases were different.

There are rules that govern these errors. If you know the rules, you can make deductions about them that bound the size of the errors in final results.

like image 3
Eric Postpischil Avatar answered Nov 08 '22 13:11

Eric Postpischil


It has been said many times before but it bears repeating: Floating point numbers are by their very nature approximations of decimal numbers. There are some decimal numbers that cannot be represented precisely due to the way the floating point numbers are stored in binary. Small but perceptible rounding errors will occur.

To avoid this kind of mess, you should always format your numbers to an appropriate number of places for presentation:

 '%.3f' % (0.3 + 0.6 + 0.1)
 # => "1.000" 

 '%.3f' % (0.6 + 0.1 + 0.3)
 # => "1.000" 

This is why using floating point numbers for currency values is risky and you're generally encouraged to use fixed point numbers or regular integers for these things.

like image 6
tadman Avatar answered Nov 08 '22 13:11

tadman