Let me show you that PHP is bad at Math:
<?php echo "0.1 + 0.2 = ". ( 0.1 + 0.2 ) ."\n"; $true = 0.1 + 0.2 == 0.3 ? "Equal" : "Not equal"; echo "0.1 + 0.2 = 0.3 => $true\n";
0.1 + 0.2 = 0.3 0.1 + 0.2 = 0.3 => Not equal
>>> 0.1 + 0.2 0.30000000000000004 >>> 0.1 + 0.2 == 0.3 false
0.1 * .1 results in 0.010000000000000002 0.7 + .1 results in 0.7999999999999999 1.1 + .1 results in 1.2000000000000002
As you can see the problem also exists when multiplying. This is because the problem does not lie in the operation, but in the way computers internally store numbers that contain a decimal point. The internal representation is called a “floating point “. This floating point representation has accuracy problems as we have shown above, but it doesn’t only apply to PHP floating points. The reason we have these accuracy problems is described in the floating point guide:
Because internally, computers use a format (binary floating-point) that cannot accurately represent a number like 0.1, 0.2 or 0.3 at all.
When the code is compiled or interpreted, your “0.1” is already rounded to the nearest number in that format, which results in a small rounding error even before the calculation happens. — floating point guide
The floating point guide also explains clearly that:
…binary fractions are different from decimal fractions in what numbers they can accurately represent with a given number of digits, and thus also in what numbers result in rounding errors:
Specifically, binary can only represent those numbers as a finite fraction where the denominator is a power of 2. Unfortunately, this does not include most of the numbers that can be represented as finite fraction in base 10, like 0.1.
Fraction Base Positional Notation Rounded to 4 digits Rounded value as fraction Rounding error 1/10 10 0.1 0.1 1/10 0 1/3 10 0.3 0.3333 3333/10000 1/30000 1/2 2 0.1 0.1 1/2 0 1/10 2 0.00011 0.0001 1/16 3/80
And this is how you already get a rounding error when you just write down a number like 0.1 and run it through your interpreter or compiler. It’s not as big as 3/80 and may be invisible because computers cut off after 23 or 52 binary digits rather than 4. But the error is there and will cause problems eventually if you just ignore it. — floating point guide
Now let’s go back to the PHP floating point calculation and evaluate this code:
<?php ini_set('precision', 17); echo "0.1 + 0.2 = ". ( 0.1 + 0.2 ) ."\n"; $true = 0.1 + 0.2 == 0.3 ? "Equal" : "Not equal"; echo "0.1 + 0.2 = 0.3 => $true\n";
0.1 + 0.2 = 0.30000000000000004 0.1 * 0.2 = 0.3 => Not equal
That is more like it. It does not solve the problem, but makes it easier to understand. Note that we have set the “precision” of the representation of floating point numbers to 17 with the “ini_set” PHP command. Gustavo Lopes explains on the php-internals mailing list why other values (like 100) do not make sense:
Given that the implicit precision of a (normal) IEEE 754 double precision number is slightly less than 16 digits , this is a serious overkill. Put another way, while the mantissa is composed of 52 bits plus 1 implicit bit, 100 decimal digits can carry up to 100*log2(10) =~ 332 bits of information, around 6 times more.
Given this, I propose changing the default precision to 17 (while the precision is slightly less than 16, a 17th digit is necessary because the first decimal digit carries little information when it is low). — source
So for now, let’s change the precision to from 14 to 17 in “/etc/php5/apache2/php.ini” on our servers and save ourselves some headaches when we are using PHP floating points.
; The number of significant digits displayed in floating point numbers. ; http://php.net/precision precision = 17
If you want more background on this topic read the excellent article “What Every Computer Scientist Should Know About Floating-Point Arithmetic“.