Subscribe to PHP Freaks RSS

Floats and money

syndicated from planet-php.net on December 6, 2018

A very common and oft-repeated programmer’s wisdom is “Don’t use floats for currency”. This always made a lot of sense to me. The idea is that floats are imprecise, due to the way they are stored.



Effectively floats are stored not by just their digits, but by a formula. This formula can’t accurately represent every fraction. The most popular way to demonstrate this is by firing up your Javascript developer console and run:



> 0.3 - 0.2
0.09999999999999998


The answer you’ll get is not 0.1, it’s 0.09999999999999998. According to many, this is easy proof why floats are bad for money.



It’s also not really something you want to put on an invoice. Some suggestions to avoid floats include:



  • Generally when dealing with money, use specialized data stores. MySQL for example has the DECIMAL field type.
  • When serializing currency values, in for example JSON, pass them around as a string, not a number.

Another very common advice is, when doing math based on currency values just forget about the floating point and count cents instead of Euros, Yen or Dollarydoos.



So instead of $ 5.00, you would simply multiply by 100 and use 500, which fits in an integer.



This is a pretty good way to deal with simple invoicing, where for the most part you’re just adding numbers.



Fractions



With these wisdoms in hand I stepped into a problem that is a bit more complex. I needed to do calculations with fractions for financial analysis.



I’ll keep my example simple, but lets say that I needed to find out what 31% of $498.09 is.



The result of this can’t be expressed as a decimal number. On your JS console it might look something like this:



154.40789999999998


This is not just due to the fact that this is a floating point number, the other is simply that this number can’t be expressed without a fraction at all. The 9’s repeat infinitely.



But this led me to think, why do I really care about this really precise number. The part I’m really interested in is $154.41, so I can just round the result and get a string:



> (input * 0.31).toFixed(2);
'154.41'


For my purposes, this was the right answer. Money isn’t expressed as fractions (beyond cents), and it’s rounded in a lot of situations.



This led me to my next realization. The 0.3 - 0.2 example was really a strawman. I can just round it to get the number I really wanted:



> (0.3 - 0.2).toFixed(2);
'0.10'


This was the correct answer all along. The fact that we needed to first get 0.09999999999999998 as an intermediate answer is kind of irrelevant, because the difference is so little, and we would have to round anyway before display.



A better example



This last exercise just made me realize a few things:



  • 0.3 - 0.2 is not a good example of why using floating points are bad. $0.00000000000000002 is not a significant difference.
  • Unless you are comfortable writing your financial reports with formulas instead of numbers, rounding is unavoidable.
  • Everybody rounds, regardless of the number system. If you use floating point math or fixed point math, you actually suffer from the exact same problem.

So I wanted to find out if there are examples where using floating-point math does become an issue. Where are its limits?



One way I thought of thinking about this, is by trying to come up with a formula where this $0.00000000000000002 difference compound into a dollar. Just multiplying it by $10,000,000,000,000,000 doesn’t seem reasonable, because that number is (currently) too high to show up in a financial calculation.



I had trouble finding good examples to demonstrate this issue, and I’m not really smart enough to come up with an idea of my own. But even

Truncated by Planet PHP, read more at the original (another 9690 bytes)