Earlier this week I came across a bug in some code that highlighted the frustration of doing comparisons with floating point numbers in PHP. Basically the code was failing when checking if one floating point number was equal to another (that was a tally of other floating numbers). Despite the two floating numbers seemingly being the same they were not equating.
Take this simplified example of the problem:-
$test = 0.1 + 0.2;
echo $test; // outputs 0.3
echo ($test == 0.3) ? 'true' : 'false'; // outputs false
Try it yourself and you’ll see it returns false. It looks as though PHP is struggling to do basic maths.
$test
is not precisely 0.3, PHP has echoed a truncated value of the actual decimal number we are working with.
Why do the numbers in $test
not add up as we’d expect? Internally PHP is using a format that cannot accurately represent certain numbers like 0.1 or 0.7. Floating point numbers are actually being stored in the binary base 2. So when we start adding up the numbers inaccuracies creep in due to the loss of precision.
It is worth noting that this issue is not unique to PHP.
As the PHP website states on its entry on Floating point numbers:-
never trust floating number results to the last digit
A simple way of thinking about this is to consider the case of a third. If we enter 1/3 into a calculator we have to convert the number into a decimal representation like 0.33. If we add up this decimal representation of a third to make a whole number, 0.33 + 0.33 + 0.33, we don’t arrive at 1 as we should. We’ve lost precision converting the fraction to a decimal.
Computers tend to use binary numbers as it is faster to perform calculations with, and in most cases the small loss of precision is negligible.
There are solutions for dealing with our original problem. One of these is to test against the smallest acceptable difference:-
$test = 0.1 + 0.2;
echo (abs($test - 0.3) < 0.1) ? 'true' : 'false'; // outputs true
Alternatively we can use PHP’s BC Math functions for handling the addition:-
// add two floating numbers,
// third parameter defines the number of decimal places
$test = bcadd(0.1, 0.2, 1);
echo $test; // 0.3
echo ($test == 0.3) ? 'true' : 'false'; // outputs true
Both these solutions will give us the expected response.
The chances are that this is an issue you will rarely come across. However, it is well worth taking into consideration, especially when working with numbers where you need the numbers to add up exactly, for example, when dealing with money.
At first, it may seem like PHP is wrong; but think about the case of handling fractions as decimals and then reconsider that internally a similar process is happening for good reason. It starts to make sense and becomes something that is worth remembering and taking into account when using floating numbers in future.