Skip to: Site menu | Main content

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

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img>
  • You can use Textile markup to format text.

More information about formatting options