Matok's PHP Blog

PHP Gotcha II: When 1 plus 7 isn't 8

article image

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:

0011111110111001100110011001100110011001100110011001100110011010

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


If you like this article then mark it as helpful to let others know it's worth to read. Otherwise leave me a feedback/comment and we can talk about it.

I'm foreigner. Where I live my friends call me Maťok.


Comments - Coming Soon