Why you shouldn’t use decimals or floats when calculating values

I still see this happening often enough in other people’s software, that I thought it was worth writing a post about.

When performing calculations in most languages, it’s always best to avoid using decimals or floats. Why is this? It’s imprecise and can result in in-accuracy, especially when dealing with lots of small numbers.

To explain, natively computers can only store integers, but need some way of representing decimals. This representation is often not entirely accurate and the reason for this is most often floating point numbers and decimals are expressed as a fraction. As anyone who has worked with fractions can tell you, you can only cleanly express these when the fraction uses a prime factor of the base. In binary this is base-2, which when you convert to a more readable base-10 ends up with repeating decimals for anything that is not a prime of the base-2.

Interestingly both MySQL and PostGreSQL treat decimals differently to floating point numbers – so decimal calculations within these two languages are accurate. So, in PostGreSQL:

SELECT 0.1 + 0.2;

Will produce

0.3

while

SELECT 0.1::float + 0.2::float;

Will produce

0.30000000000000004

It can be difficult to spot if you are not debugging effectively too. For example both Python 2 and PHP will handily convert some calculations to strings if you echo or print. So in PHP if you do:

echo .1 + .2;

You will get

0.3

as an output (unless you’ve explicitly set precision to 17 points), but if you are debugging correctly and do:

var_dump(.1 + .2);

you will get

float(0.30000000000000004441)

In Python 2, doing

print .1 + .2

Will get you

0.3

However this has been fixed in Python 3, and will instead output the float.

It’s no coincidence that the likes of Barclays and Stripe always use integer values in their API’s and insist that you send whole integers back as values. If you are writing anything that involves calculations, you should always convert them to integers first – or ideally store them as integers and just format them for view as appropriate.