PHP: математические вычисления и сравнения

Я ожидал бы, что все приведенные ниже сравнения будут bool (true), но это не так. Может кто-нибудь объяснить это?

test.php

<?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 

Solutions Collecting From Web of "PHP: математические вычисления и сравнения"

Числа с плавающей запятой имеют ограниченную точность. Хотя это зависит от системы, 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');