PHP Gotcha II: When 1 plus 7 isn't 8
Look at this simple addition:
$a = 0.1;
$b = 0.7;
if ($a + $b == 0.8) {
echo '0.1 + 0.7 is 0.8';
} else {
echo '0.1 + 0.7 is NOT 0.8';
}
The result is: 0.1 + 0.7 is NOT 0.8
Actually, this is not matter of PHP. This is how floating point arithmetic works.
How real numbers are stored?
You can find big red warning on manual page. Important is this information:
PHP typically uses the IEEE 754 double precision format
Nice article about this topic is on wikipedia, but what about trying yourself? Here is tinny C program:
#include <stdio.h>
#include <stdlib.h>
void print_bits(void *a, size_t size);
int main()
{
double number;
printf("Gimme float number: ");
scanf("%lf", &number);
printf("\n");
print_bits(&number, sizeof(number));
printf("\n");
return EXIT_SUCCESS;
}
void print_bits(void *address, size_t size)
{
int i;
int b;
address += size-1;
printf("Printing %zu bytes:\n", size);
for (i = size-1; i >= 0; i--) {
for (b = 7; b >= 0; b--) {
printf("%d", (*(char *)address & (1<<b))>>b);
}
address--;
}
}
You can download it from GitHub. When I enter number 0.1
on input I got this output:
According to wikipedia article:
First 0
is sign and this means positive number. Next 11 bits 01111111011
is exponent. Rest of this number 001100110011001100110011001100110011001100110011010
is fraction.
Exponent 011111110112
is in decimal 1019
, but we must subtract 1023
so exponent is -4.
At the begging of fraction we put 1
and we get a number 110011001100110011001100110011001100110011001100110102
or 720575940379279410
in decimal format is fraction.
Equation to concrete real number is:
2-4 * (7205759403792794 * 2-52)
2-56 * 7205759403792794
0.10000000000000000555111512312578 = 0.1
0.69999999999999995559107901499374 = 0.7
(computed by same principles)
Conclusion
When I add this two numbers I get 0.799999999999999961142194138119521 and that is really close to 0.8 but it isn't exactly equal. This is how real numbers are stores in computer memory no matter of programming language you are using.
If you are working with float you must consider this behavior, avoid using ==
or ===
with real numbers. You are safe if you do sum in MySQL on decimal type or use some kind of math library which can handle this float precision issue.
Footnotes:
1: All calculations were made by calculator, some last significant may be stripped - but it isn't important
I'm foreigner. Where I live my friends call me Maťok.