We use computers every day to solve mathematical issues, from how much taxes should I apply to my users’ basket to computing next week’s weather forecast.
Solving most of those problems is nowhere near easy, and would be harder still if we didn’t use mathematics to reason, represent and compute insane amounts of data.
But the code we execute on our computers isn’t exactly the same as the doing the maths we try to represent.
For instance, the plus operator does not yield the same results as a real addition.
Don’t believe me? Run
Spoiler alert: you won’t get 0.3 (if you do, let me know which browser you are using).
This issue comes from how computers represent decimal numbers, you could try doing the same addition with python, C… and the result would be
This rounding error is caused, deep down, by the fact that our computer only can store a finite amount of data (ref. needed) when there is an infinite amount of decimal numbers (ref. needed).
Some languages, like PHP (tested with PHP 5.5.14) will actually return 0.3 for 0.1 + 0.2.
In this case do we really have a solution? Were PHP developers able to solve this problem many other languages developers failed to? Of course not, the result was simply rounded, which can be made apparent by running some other cleverly crafted additions:
0.30000000000000004 - 0.3 would yield
0.30000000000000004 - (0.1 + 0.2) would yield
One could argue that, to display the taxes amount on an amazon basket, rounding to two digits would be more than enough (on all the monetary systems I know of), and a
0.00000000000004 error on the displayed amount would be of little to no consequence.
Floats are a deadly issue, literally. See the Patriot Missile Failure on February 25, 1991, which caused the death of 28 soldiers. The main cause for that failure was that 0.1 is not a round number, as far as floats are concerned.
So yes, the gap between the mathematics we use to reason about software and its implementation can be a problem, and a deadly one.
Some of those problems have been solved on some languages using rational arithmetic instead of floating point arithmetic.
Using rational arithmetic does have a performance (both in terms of CPU and memory) impact, and does not solve all rounding errors (specially with irrational numbers).
The only solution I can recommend is to always assume that a floating point operation is inexact and be ready to handle the consequences, which means:
- Not using the equality operator with floats, ever
- Always round/floor/whatever the result you display to the user (no one wants to see 5.55111512313e-17°C appear as the tomorrow’s temperature)
- Avoid using floats altogether if possible (for example, one could use integers to represent cents instead of float value for dollars, euros or any other currency that could have decimal values)
As we’ve seen, the implemented operators does not yield the same results as their mathematical counterparts, and they also does not share the same properties.
- The + is not associative (with and associative operator,
a + (b + c) = (a + b) + c)
- The == (equality) is not transitive (if it was, that would mean that if
a == band
b == cthen
a == c)
- The == does not even have to be reflexive (by design in C)
You liked this article? You'd probably be a good match for our ever-growing tech team at Theodo.