2010-01-03 5 views

ответ

5

Это одна из проблем с представлением чисел с плавающей запятой IEEE 754; представления недостаточно точны, чтобы представлять все рациональные числа.

Способ сделать это, чтобы сравнить разницу в отношении очень небольшого числа для близости, а не равенство:

abs(($onethird + $fivethirds) - ($half + $threehalf)) < 1e-8 
+1

Это характер чисел с плавающей запятой в целом, так что это связано с стандартом IEEE 754? –

+0

PHP написан на языке C и поэтому получает от него поддержку FP, и в большинстве случаев C использует IEEE 754. Кроме того, IEEE 854 использует переменную radix и поэтому может поддерживать более высокую точность для некоторых номеров за счет некоторой скорости обработки, когда вычисляя с ними. –

+0

Это характер чисел с плавающей запятой в целом. Значение epsilon (здесь 0.000000001) зависит от количества бит, представляющих поплавок. – slebetman

4

Проблема возникает из-за небольших ошибок, введенных Floating Point arithmetic. Это не относится к PHP.

Вы можете исправить это, введя небольшой коэффициент «допусков», то есть, проверив, что первое значение в компарадионе равно> = второе значение минус допуск, а < = второе значение плюс допустимое отклонение.

1
var_dump(abs($onethird + $fivethirds - $half + $threehalf) < 0.00001); 

см: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm также: http://docs.sun.com/source/806-3568/ncg_goldberg.html

Это верно на всех языках программирования.

Я еще не пришел на любой язык, где это делается автоматически, когда программисты хотят равенства поплавков. Кто-то должен прийти с новым оператором, возможно = ~ = для поплавочной равенства, которое будет автоматически делать сравнение дифф с эпсилон:

if ($float1 =~= $float2) {... 

Это раздражает, что каждый год, так как я закончил в 2000 году, в это время того года, какой-то новичок задаст этот вопрос в какой-то новостной группе, форуме или списке рассылки. Только в прошлом месяце я ответил на это на comp.lang.tcl. И это не просто новички, два месяца назад я должен был объяснить это своему коллеге, который более 5 лет разрабатывает программное обеспечение, спрашивая меня, почему его код Perl не работает.

1

Как исправить эту проблему?

Проблема, которую вы хотите решить? Ваш код просто показывает, что числа с плавающей запятой имеют ограниченную точность - это ничего нового.

Для большинства приложений реального мира вход, который не является 100% точным в любом случае, и результат должен быть точным до нескольких десятичных знаков. Сравнения равенства просто не являются чем-то, что вам нужно большую часть времени. Если вы это сделаете, вы можете выманить его, увидев, находится ли результат в пределах некоторого предопределенного небольшого расстояния от заданного числа.

Если вам нужна десятичная математика с определенной точностью, используйте функции BC Math - но поймите, что они очень медленны, если они используются для сложных вычислений.

Смежные вопросы