Floats are not exact. This surprises beginners, but it happens in every programming language, because computers store numbers in binary.
<?php
var_dump(0.1 + 0.2);
var_dump(0.1 + 0.2 === 0.3);
float(0.30000000000000004)
bool(false)
Just as 1/3 cannot be written exactly in decimal (0.3333…), 0.1 cannot be written exactly in binary.
Echo hides the problem
echo rounds to a precision of 14 digits, so the problem is not visible.
<?php
echo 0.1 + 0.2;
0.3
Do not trust what echo shows. The stored number is slightly different.
A famous trap
<?php
$value = (0.1 + 0.7) * 10;
echo (int) $value . "\n";
var_dump($value);
7
float(7.999999999999999)
You expected 8. The cast cut the number off at 7. When you must turn a float into a whole number, round it first.
<?php
$value = (0.1 + 0.7) * 10;
echo (int) round($value);
8
Comparing floats
Never compare floats with ==. Check whether they are close enough.
<?php
function almostEqual(float $a, float $b, float $epsilon = 1e-9): bool
{
return abs($a - $b) < $epsilon;
}
var_dump(almostEqual(0.1 + 0.2, 0.3));
bool(true)
Money
Do not store money as floats. Two safe options:
1. Integers in the smallest unit.
<?php
$price = 1999; // 19.99 stored as 1999 cents
$quantity = 3;
$total = $price * $quantity;
echo number_format($total / 100, 2);
59.97
2. BCMath, a PHP extension that does exact decimal maths on strings.
<?php
echo bcadd("0.1", "0.2", 2) . "\n";
echo bcmul("19.99", "3", 2);
0.30
59.97
Huge integers
Integers above PHP_INT_MAX become floats and lose digits. For exact big numbers, use bcmath or gmp.
Rule of thumb
==.