$onethird = 1.0/3;
$fivethirds = 1.0/3+1.0/3+1.0/3+1.0/3+1.0/3;
$half = 1.0/2;
$threehalf = 1.0/2+1.0/2+1.0/2;
var_dump($onethird + $fivethirds == $half + $threehalf);
какие выводы false
, но поскольку все мы знаем:5/3+1/3=2=3/2+1/2
Как решить эту проблему?
Это одна из проблем IEEE 754 представления чисел с плавающей точкой; представления недостаточно точны для представления всех рациональных чисел.
Способ сделать это - сравнить разницу с очень малым числом для близости, а не равенства:
abs(($onethird + $fivethirds) - ($half + $threehalf)) < 1e-8
Это зависит от того, насколько точным должно быть ваше сравнение.
В этом случае следует использовать bccomp() вместо "==".
см. http://php.net/manual/en/language.types.float.php
var_dump(abs($onethird + $fivethirds - $half + $threehalf) < 0.00001);
see: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm И ещё: http://docs.sun.com/source/806-3568/ncg_goldberg.html
Это справедливо для всех языков программирования.
Я еще не знаю ни одного языка, где это делается автоматически, когда программисты хотят равенства с плавающей точкой. Кто-то должен придумать оператор new, возможно =~= для равенства float, который автоматически делал бы сравнение diff с epsilon:
if ($float1 =~= $float2) {...
Раздражает то, что каждый год, с тех пор как я закончил в 2000 году, примерно в это время года какой-нибудь новичок будет задавать этот вопрос в какой-нибудь группе новостей, на форуме или в списке рассылки. Только в прошлом месяце я ответил на этот вопрос на сайте comp.lang.tcl. И это не только новички, два месяца назад я должен был объяснить это моему коллеге, который разрабатывал программное обеспечение более 5 лет, спрашивая меня, почему его Perl-код не работает
.Как решить эту проблему?
Какую актуальную проблему вы хотите решить? Ваш код просто показывает, что числа с плавающей точкой имеют ограниченную точность - в этом нет ничего нового.
Для большинства реальных приложений, вход, который все равно не является 100% точным, и результат должен быть точным до пары знаков после запятой. Сравнения равенства просто не нужны большую часть времени. Если нужно, то можно подтасовать его, посмотрев, находится ли результат на некотором предопределенном малом расстоянии от заданного числа.
Если вам нужна десятичная математика с определенной точностью, используйте функции BC Math - но имейте в виду, что они очень медленные, если используются для сложных вычислений.
.Проблема связана с небольшими ошибками, введенными арифметикой с плавающей точкой . Это не специфично для PHP.
Это можно исправить введением небольшого коэффициента "толерантности", т.е. проверкой, что первое значение в сравнении >= второе значение минус допуск и <= второе значение плюс допуск.