Back to Blog
PHP Development

Floating-Point Arithmetic - Why is (int)((0.7+0.1)*10) = 7 ?

The problem

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

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)

The same thing happens 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 calculations in binary; floating-point numbers are represented as follows:

Format Size Sign Exponent Mantissa
IEEE Short Real (single precision) 32 bits 1 bit 8 bits 23 bits
IEEE Long Real (double precision) 64 bits 1 bit 11 bits 52 bits

Numbers that can easily be represented in binary are 1/(2^1), 1/(2^2), 1/(2^3), 1/(2^4), etc., because they have a mantissa equal to 1 (encoded as 0).

A number's value can only be represented exactly if it can be expressed by the formula exponent * mantissa. The mantissa is the number the exponent is multiplied by, and its value is 1 + 1/rb, where rb is the reverse binary interpretation.

For example, take the number 3.5: 3.5 = sign(1) * exponent(2) * mantissa(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).

Why does this happen?

Using an IEEE 754 converter, it turns out that:

  • 0.7 is actually represented as 0.699999988079071
  • 0.1 is actually represented as 0.10000000149011612

Adding these two values gives 0.7999999895691871. Multiplying by 10 gives 7.999999895691871, which when cast to int is 7 - the same way 3.5 is 3 when cast to int.

The 0.6 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 you use echo and var_dump, or apply mathematical operations, PHP automatically adjusts the values - but intval and casting to int work on the underlying bits before those 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 important for your project, you can get correct results by using the BCMath PHP Extension.

Frequently Asked Questions

Why does (int)((0.7+0.1)*10) return 7 instead of 8? +

Because 0.7 is actually represented internally as 0.699999988079071 and 0.1 as 0.10000000149011612. Adding them gives 0.7999999895691871, and multiplying by 10 gives 7.999999895691871, which truncates to 7 when cast to int.

Does the same rounding issue happen with 0.6 instead of 0.7? +

Yes. 0.6 is represented as 0.6000000238418579, and (0.6000000238418579 + 0.10000000149011612) * 10 equals about 7.00000025331974, which also truncates to int(7).

Why do var_dump and echo show the expected value while intval/int casting does not? +

When using echo, var_dump, or mathematical operations, PHP automatically adjusts the displayed values. However, intval and casting to int operate on the underlying bits before those values were adjusted, which is why they can produce a different (truncated) result.

How are floating-point numbers represented at the CPU level? +

The CPU performs calculations in binary. IEEE Short Real (single precision) uses 32 bits: 1 sign bit, 8 exponent bits, and 23 mantissa bits. IEEE Long Real (double precision) uses 64 bits: 1 sign bit, 11 exponent bits, and 52 mantissa bits. A number can only be represented exactly if it can be expressed as exponent * mantissa; numbers like 0.99999999 cannot be represented exactly.

Is there a way to get accurate results for calculations like this in PHP? +

If precise values matter for your project, the article recommends using the BCMath PHP Extension to get correct results.