Float is evil
by Dominique L., on 24 April 2012
Base 2 representation of real numbers is to me an artifact of the time when every single bit and every single flop mattered, but I have to admit that with the memory and computing power we have today, I don’t understand why modern languages do not use base 10 representation by default.
Let me explain. It is important to remember that a number with a finite base 10 representation does not necessarily have a finite base 2 representation, and conversely. Of course, this problem is not inherent to base 2 but the fact is, in everyday life - some geeks apart - we compute in base 10: we expect 1/3 to have an infinite number of digits after the decimal point, not 12/10. In that respect, base 2 can sometime give some surprising results. To convince yourself, just type the following line in your Ruby console:
For most computations errors are maybe small enough to ignore, but there is always the risk that they will add up. If your application handles money one way or another, this can become a real problem (accountants don’t like cents appearing or disappearing without reason).
The not so good idea: working with cents
I often read that to avoid base conversion errors when dealing with money, you should work in cents, as to only use integers. This may look like a simple and elegant solution, but it simply doesn’t work for two simple reasons.
First, the cent is not always the smallest unit of money: just think about an application dealing with gas pump prices or exchange rates. Second, even if it is the case in your application, or you are working with nanocents, you will eventually end up with real numbers: divide 12 cents by 10 (e.g. to compute a mean) and you will be back at square one.
Decimal representation in your code and the database
Ruby and SQL allow you to store numbers in their decimal representation, using the
BigDecimal and the
decimal (sometimes also called
numeric) field type, respectively. Behind the scenes,
BigDecimal both use a floating-point representation of the form
significant_digits x base^exp; the difference being that the base is 2 if the first case, 10 in the second.
BigDecimal, your calculations will therefore always return what a human being expects. Moreover, you can gain total control on your mathematical operations by specifying the precision (number of significant digits), scale (digits after the dot), and rounding strategy, ensuring you will never get unexpected results.
BigDecimal also has the ability to represent special numbers such as Infinity or NaN.
On the database side, just use the
decimal type in your migration and specify a
scale (defaults can differ), ActiveRecord will take care of the rest. Even better: data from
decimal fields will automatically by retrieved into