Progamming Ruby: The dangers of comparing Floats (and of programming tutorials with mistakes in them)
I’m training an out of college programmer in Ruby and Rails. After a week of coding just Ruby, I made her complete her first Rails tutorial: Developing Rails Applications On Mac OS X Leopard
def total_expenses
expenses.sum(:amount) || BigDecimal("0.0")
end
At the end of to tutorial, the programmer is supposed to learn about unit testing and is building a test_budget:
def test_budget
#...
assert_equal BigDecimal("30.50"), event.total_expenses
#...
end
The problem is, one of the tests was failing, even though the apple web page showed that the same code ran fine on their computers: “4 tests, 6 assertions, 0 failures, 0 errors”. Our 5th assertion failed.
Turns out, the function expenses.sum(:amount) returned a Float object. Because of rounding errors, comparing that Float object with a BigDecimal lead to a inequality.
>> BigDecimal(“30.5”) == 30.5
=> false
For Fixnum, it works fine:
>> BigDecimal(“30”) == 30
=> true
Other Floats don’t work either:
>> BigDecimal(“30.4”) == 30.4
=> false
But, surprise, some Floats are equal on our architecture with the BigDecimal.
>> BigDecimal(“30.3”) == 30.3
=> true
My results were on tested on a 32 bit x86 Windows XP and a Linux with Ruby 1.8.6 and Rails 2.1.
This the fixed function in the Event class that leads to consistent results no matter what:
def total_expenses
BigDecimal(expenses.sum(:amount).to_s) || BigDecimal("0.0")
end

Post new comment