UPDATE: See the follow-up here.
Ok. I don’t really pretend to know why this is happening, but Chad and I have verified this on several operating systems. And, yes, the number we found was totally by accident.
To get weirded out, just perform the following in irb:
s = "40.87" f = s.to_f ft = f * 100 fti = ft.to_i
If you are like us (Ruby version 1.8.6, patchlevel tested at 0 and 114), your end result there is 4086.
When we started digging a little deeper, the float value stored in the “ft” variable is actually less than 4087, but greater than 4086.9999999999995. We cast around looking for other numbers where this happens, but have been unable to find any.
If anybody has a good explanation, we’d love to hear it.
Oh, and to fix it? Just use:
ft.round.to_i
Of course!
Comments
to_i truncates any decimal portion http://www.ruby-doc.org/core/classes/Float.html#M000550
Yes. This is due to the nature of floating point numbers. Floating point numbers on most platforms are represented by the format as defined in IEEE 754-1985. Floating point numbers, as humans use them, are base 10. But floating point numbers in computers are base 2, and not all base 10 floating point numbers can be accurately represented by base 2. For example, 0.6 cannot be represented in base 2 with a finite number of bits. If you try to do the conversion then you will end up with an infinite number of bits after the decimal point.
Computer programmers must always keep this fact in mind. Floating point calculations are never entirely accurate, and depending on how they’re done, some information will be lost. That’s why the financial world don’t use floating point numbers to represent money. Instead, they use integers.
A follow-up to my last comment. Wikipedia explains the problem clearly: http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
I think this would fall under the guise of “What Every Computer Scientist Should Know About Floating-Point Arithmetic”: http://docs.sun.com/source/806-3568/ncg_goldberg.html
Basically, rounding errors in the conversions between base-2 and base-10.
Python gives the same results: >>> int(40.87 * 100) 4086
I’m not a math wizard, but I think this would be expected in any language. Float operations simple aren’t predictable, so every operation causes you to lose some accuracy/precision.
Here’s a fun one:
This is a commonly known issue with floating point operations in just about any language. See http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems for more details. Here’s a clearer illustration of the same issue:
irb(main):007:0> puts 'not equal' unless (3.2 - 2.0) == 1.2So, uh, use big decimal when you need real precision. =)
Ok, I’ve clarified in the followup post. Sheesh, nothing better than looking dumb in public! ;-)