This article applies to PHP 5.x but also to PHP 7

While using floating-point arithmetic you might have noticed that not all the calculus results are as expected, this can usually be observed when casting values.

So the output for (0.7 + 0.1) * 10 is:

var_dump((0.7+0.1)*10); # float(8)
var_dump(intval((0.7+0.1)*10)); # int(7)

Now let’s try it with 0.6 instead of 0.7

var_dump((0.6+0.1)*10); # float(7)
var_dump(intval((0.6+0.1)*10)); # int(7)

How does the CPU understand these numbers?

The CPU makes the calculations binary, the floating point numbers are represented as follows:

IEEE Short Real: 32 bits     1 bit for the sign, 8 bits for the exponent, and 23 bits for the mantissa. Also called single precision.
IEEE Long  Real: 64 bits     1 bit for the sign, 11 bits for the exponent, and 52 bits for the mantissa. Also called double precision.

The numbers that can easily be represented binary are: 1/(2^1). 1/(2^2) . 1/(2^3), 1/(2^4) etc. This because they have mantissa equal to 1 (enconded as 0).

The number values can only be represented exactly if they can be represented by this formula:  exponent * mantissa

The mantissa is the number which the exponent is multiplied to. The mantissa value is 1 + 1/rb, where rb is the reverse binary interpretation

Let’s take the number 3.5 for example.

The float numbers are sign * exponent * mantissa.  3.5 = 1 * 2 * 1.75 .

Mantissa: 1 + 11000000000000000000000 -> 1/(2^0) + 1/(2^1) + 1/(2^2) + 0*(2^3) + … + 0*(2^23) -> 1 + 0.5 + 0.25 -> 1.75

Some numbers cannot be represented exactly (such as  0.99999999)

To learn more about how IEEE Real numbers are formed follow this link.

Why does this happen?

Using this IEEE 754 converter we have found out that:

0.7 is actually represented as 0.699999988079071

0.1 is actually represented as 0.10000000149011612

If we add these two values we will obtain 0.7999999895691871

And if we multiply it by 10 we obtain 7.999999895691871 which if casted to int is 7 the same way 3.5 is 3 if casted to int.

The other example still shows 7 because 0.6 is actually represented as 0.6000000238418579 and  (0.6000000238418579 + 0.10000000149011612)*10 is 7,00000025331974

 

But still …

If we use echo and var_dump or if applying mathematical operations, PHP automatically adjusts the values, but intval and casting to int work on their bits before the values were adjusted


var_dump((0.7+0.1)*10); # float(8)
var_dump(intval( ((0.7+0.1)*10) ) );   # int(7)
var_dump(intval( ((0.7+0.1)*10)+1 ) ); # int(9)

If these values are very important for your project you can get the correct values we by using the BCMath PHP Extension.

For a more technical & mathematical approach read this document.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>