This project is archived and is in readonly mode.

#564 ✓invalid
Tomash

dangerous float operations

Reported by Tomash | July 7th, 2008 @ 03:46 PM | in 2.x

While implementing storage of price/cost/balance in cents I've stumbled upon one nasty bug. Consider the following code:

class Account < ActiveRecord::Base
def balance=(amount)
  write_attribute(:balance, amount.to_f*100.0)
end
end

(analogous accessor code omitted)

It works as expected... usually. That is, there's one magical number - 4.6 - which sets the field balance not to 460, but to 459, thus screwing up the whole purpose of this.

4.6 is the only magical number I could find - others work like charm and expected.

In Ruby (not on Rails) works as expected:

class Act
  def balance=(amount)
    @balance = amount.to_f*100
  end
end

that is, for 4.6 works like for all the other numbers.

EDIT: This problem seems to be related to to_i method called on floats. AND it seems to be Ruby-related

irb(main):005:0> (895.0).to_i
=> 895
irb(main):006:0> (8.95*100).to_i
=> 894

(yes, I've just discovered that to_i just cuts away everything after the point)

Workaround: add a tiny number to the converted one OR use .round

irb(main):007:0> (8.95*100 + 0.0000001).to_i
=> 895
irb(main):007:0> (8.95*100).round
=> 895

Proposed solution: make field accessors for integer fields use .round instead of to_i.

As for now, I'm good with adding .round in my custom accessor.

Comments and changes to this ticket

  • Tomash

    Tomash July 7th, 2008 @ 03:37 PM

    Update: 8.95 is also a "magical number" inside ActiveRecord, i.e. balance=8.95 sets balance field to 894. Other numbers "around" seem OK.

    >> a.balance=(8.95)
    => 8.95
    >> a.balance
    => 894
    >> a.balance=(8.93)
    => 8.93
    >> a.balance
    => 893
    
  • isak

    isak August 7th, 2008 @ 01:23 PM

    There is no bug, floating point numbers are inaccurate by design.

    The only reason people muck around with cents for money values is to avoid the floating point issue. This approach really hasn't been viable since 1.2, when ActiveRecord finally got support for decimal columns, but it seems like many pick it up from outdated tutorials floating around on the web.

  • Jim Lindley

    Jim Lindley August 9th, 2008 @ 09:58 PM

    Tomasz,

    There's more info on the details here, but isak is right - storage of floating point numbers in binary representation is less accurate then you'd think.

  • Frederick Cheung
  • Tomash

    Tomash August 10th, 2008 @ 04:32 PM

    I know, I know. We're using INT columnt because of that and convert numbers on-the-fly by overloading amount and amount= methods in our models. But I've just realised it's a bad approach and we should just represent them as ints also internally, doing the "conversion" only on the view level.

    Sorry for the hassle, You can close it as invalid/wontfix/reporter_incompetent ;)

  • Tarmo Tänav

    Tarmo Tänav September 18th, 2008 @ 05:53 AM

    • State changed from “new” to “invalid”
    • Tag changed from activerecord, bug to activerecord, bug

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Pages