Search code examples
rubybigdecimalfloating-accuracy

floating point error in Ruby's BigDecimal class?


The universal advice to avoid floating point errors in ruby is to use BigDecimal. I must be overlooking something, because I think I've found a case where BigDecimal math is returning an error where a Float does not:

using Float gives the correct answer of 2.75:

> 50.0 * 0.6 / 360.0 * 33
=> 2.75

using BigDecimal gives the incorrect answer of 2.74999999:

> BigDecimal("50") * BigDecimal("0.6") / BigDecimal("360") * BigDecimal("33")
=> #<BigDecimal:7efe74824c80,'0.2749999999 999999989E1',27(36)>

Someone please tell me what I'm missing here?


Solution

  • Let's simplify your example, and use this one instead:

    BigDecimal(1) / BigDecimal(3) * BigDecimal(3)
    # => #<BigDecimal:19289d8,'0.9999999999 99999999E0',18(36)>
    

    How did it get there?

    BigDecimal(1) / BigDecimal(3)
    # => #<BigDecimal:1921a70,'0.3333333333 33333333E0',18(36)>
    

    BigDecimal does not provide rational numbers, so when you divide 1 by 3, you get 0, following by a lot of 3s. A lot, but not infinitely many. When you then multiply that by 3, you will get 0 followed by equally many 9s.

    I believe you misread the BigDecimal's advertisement (although I am not sure it is anywhere advertised as the solution to floating point errors). It just provides arbitrary precision. It is still a floating point number. If you really want exact numbers when dividing numbers, you might take a look at Rational class:

    (Rational(50) * Rational(0.6) / Rational(360) * Rational(33)).to_f
    # => 2.75