This project is archived and is in readonly mode.
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 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 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 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 August 10th, 2008 @ 12:18 AM
A classic paper on this is http://docs.sun.com/source/806-3...
-
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 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>