Сумма 0.1 и 0.2 НЕ равняется 0.3

Странное дело, но это правда — сумма .1 и .2 не будет равняться .3.

Рассмотрим на примере:

if ((0.1 + 0.2) == 0.3) {
    echo '1';
} else {
    echo '0';
 }

Вы получите 0. На первый вгляд это странно и возможно нужно явно указать, что мы работаем с числами с плавающей точкой:

if (((float)0.1 + (float)0.2) == (float)0.3) {
    echo '1';
} else {
    echo '0';
 }

Результатом подобного сравнения также будет 0. А что если продебажить результат выражения?

var_dump(0.1 + 0.2);

// результат будет: float(0.3)

Как видим мы получаем в итоге тип float и значение 0.3.

Так в чем же дело? Почему так происходит?

Суть проблемы сравнения состоит в том, что в данном случае мы работаем с числами округляя их до десятых и чтобы видеть картину целиком, давайте округлим до… 17(!)го знака:

ini_set('precision', 17);

var_dump(0.1 + 0.2);

// Результат будет: float(0.30000000000000004)

Немного странный результат, правда? А все потому, что Вы видите и работате с простыми дробями, но компьютер оперирует двоичными числами. Таким образом перед выполнением выражения он переводит десятичное представление чисел в двоичное — нули и единицы.

Затем следует знать, что при переводе в двоичную систему, некоторые числа имеют бесконечную (либо очень большую) дробь, а так как на хранение чисел выделяется ограниченное кол-во памяти в системе — они окргуляются/обрезаются по стандарту IEEE 754. И вот накопленные округления дают на выходе такие погрешности и как результат — неточность вычисления.

Конкретно в нашем примере проблема с 0.1 — это число не может быть точно представлено в двоичной системе.

Так как сравнить сумму 0.1 + 0.2 с 0.3?

Вариант №1.

Для функции bcadd необходимо добавить библитеку PHP — php7.1-bcmath. В противном случае вы получите ошибку уровня Fatal.

// функция вернет сумму двух чисел с плавающей точкой и с указанным огруглением
$test = bcadd(0.1, 0.2, 1);

var_dump($test);

// результатом будет: string(3) "0.3"

Результат будет типа string!

if ($test == 0.3) {
    echo '1';
} else {
    echo '0';
 }

PHP сам приведет $test к нужному типу данных и в этом случае результатом сравнения будет 1

Вариант №2

$test = 0.1 + 0.2;

if (abs($test - 0.3) < 0.1) {
    echo '1';
} else {
    echo '0';
}

Данное выражение также даст нам положительный результат сравнения.