Я ожидал бы, что все приведенные ниже сравнения будут bool (true), но это не так. Может кто-нибудь объяснить это?
<?php $f = 12; $f += 5.95; $f += 5.95; $f += 5.95; echo 'var_dump($f) = '; var_dump($f); echo 'var_dump($f == \'29.85\') = '; var_dump($f == '29.85'); echo 'var_dump($f == 29.85) = '; var_dump($f == 29.85); echo 'var_dump($f == (float)\'29.85\') = '; var_dump($f == (float)'29.85'); echo 'var_dump($f == \'29.85\') = '; var_dump((string)$f == '29.85'); echo 'var_dump(round($f, 2) == \'29.85\') = '; var_dump(round($f, 2) == '29.85');
$ php test.php
var_dump($f) = float(29.85) var_dump($f == '29.85') = bool(false) var_dump($f == 29.85) = bool(false) var_dump($f == (float)'29.85') = bool(false) var_dump($f == '29.85') = bool(true) var_dump(round($f, 2) == '29.85') = bool(true)
$ php -v
PHP 5.2.14 (cli) (built: Jul 23 2010 15:23:00) Copyright (c) 1997-2010 The PHP Group Zend Engine v2.2.0, Copyright (c) 1998-2010 Zend Technologies with Xdebug v2.1.0, Copyright (c) 2002-2010, by Derick Rethans
Числа с плавающей запятой имеют ограниченную точность. Хотя это зависит от системы, PHP обычно использует формат двойной точности IEEE 754, который даст максимальную относительную ошибку из-за округления порядка 1.11e-16. Неэлементарные арифметические операции могут приводить к большим ошибкам, и, конечно же, необходимо прообразить ошибку, когда несколько операций усугубляются.
Кроме того, рациональные числа, которые точно представлены в виде чисел с плавающей запятой в базе 10, например 0,1 или 0,7, не имеют точного представления в виде чисел с плавающей запятой в базе 2, которые используются внутри, независимо от размера мантиссы. Следовательно, они не могут быть преобразованы в их внутренние двоичные копии без небольшой потери точности. Это может привести к запутывающим результатам: например, пол ((0,1 + 0,7) * 10) обычно возвращает 7 вместо ожидаемого 8, так как внутреннее представление будет чем-то вроде 7.9999999999999991118 ….
Поэтому никогда не доверяйте действию чисел с плавающим числом до последней цифры и никогда не сравнивайте числа с плавающей запятой для равенства.
Страница документации по PHP
Когда вы сравниваете значения как строки, обе стороны равны « '29.85'
, поэтому вы становитесь true
. До сих пор так легко.
Сравнение по числовому значению приводит вас к земле двоичных представлений значений с плавающей запятой. Поскольку числа хранятся в базе-2, любое действительное число, которое не выражается в конечном двоичном разложении, не может быть точно представлено числом с плавающей запятой.
Другими словами, каждое число, которое не может быть записано как часть целых чисел, где знаменатель является степенью 2, не может быть таким образом представлен. Это включает 1/5 и 1/10 и 597/20 (что составляет 29,85).
Поскольку эти числа не могут быть точно представлены, результат операций, связанных с такими числами, зависит от порядка операций и ошибок округления и усечения, и поэтому, например .1 + .1 + .1
не совпадает с .3
и подобный для ваших вычислений.
Как говорит @Shakti Singh, числа с плавающей запятой не сохраняются точно, поэтому я бы предположил, что $ f фактически хранится как что-то вроде 29.849999999999999 …, поэтому не совсем точно равно 29.85.
Кроме того, эта строка кода выглядит неправильно:
echo 'var_dump($f == \'29.85\') = '; var_dump($f == 29.85);
Я предполагаю, что это должно быть:
echo 'var_dump($f == \'29.85\') = '; var_dump($f == '29.85');