Странное добавление числовых строк в PHP - PullRequest
1 голос
/ 08 декабря 2011

Я складываю вместе две числовые строки $ a и $ b, а затем сравниваю результат с другой числовой строкой $ c.Все три числа хранятся в виде строк и преобразуются в числа с плавающей точкой PHP на этапе сравнения.

По какой-то причине тест $ a + $ b == $ c не оценивается как истинный, хотя и должен.

Вы можете воссоздать проблему с этимскрипт:

<?php
$a = "-111.11";
$b = "-22.22";
$c = "-133.33";

echo '$a is '.$a."\n";
echo '$b is '.$b."\n";
echo '$c is '.$c."\n";
echo '$a + $b is '.($a+$b). "\n";

if ($a + $b == $c) {
    echo 'a + b equals c'."\n";
} else {
    echo 'a + b does not equal c'."\n"; 
}
?>

Странно, если я немного изменю значения так, чтобы $ a = -111.11, $ b = -22.23 и $ c = -133.34, он работал как положено.1012 * Я что-то упускаю из виду или это ошибка в PHP?

Ответы [ 3 ]

5 голосов
/ 08 декабря 2011

Из большого красного поля на этой странице: http://php.net/manual/en/language.types.float.php

никогда не сравнивать числа с плавающей точкой на равенство.

По сути, вы не получаете правильные числа, потому что они сохраняются в несколько ином формате, поэтому при сравнении вы получаете ошибки

Эта ссылка @Corbin действительно хороша, так что я добавляю ее только ради любви :)
http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

Что должен знать каждый компьютерщик об арифметике с плавающей точкой

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

2 голосов
/ 08 декабря 2011

Вы сталкиваетесь с ограничением арифметики с плавающей запятой. Точно так же, как есть определенные числа, которые вы не можете представить точно в десятичном виде (например, 1/3), есть некоторые числа, которые вы не можете точно представить в двоичном виде с плавающей запятой.

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

Вы можете сделать это, вычтя одно число из другого и увидев, что абсолютный результат ниже вашего порога (в моем примере 0,01):

if (abs ($someFloatingPointNumber - $someOtherFloatingPointNumber) <= 0.01)
{
    // The values are close enough to be considered equal
}

Конечно, это в сочетании с ошибками округления, которые могут появиться при последовательных математических операциях, означают, что числа с плавающей запятой часто не всегда являются лучшим выбором, и их следует избегать, где это возможно. Например, если вы имеете дело с валютой, сохраняйте значения в виде целых чисел в младшей единице (пенни за фунты стерлингов, центы за доллары США и т. Д.) И конвертируйте их только в основную единицу, разделив на 100 для отображения.

1 голос
/ 08 декабря 2011

Ваш номер всегда имеет две десятичные позиции?

Если так, вы можете попробовать это:

$aDec = round($a * 100);
$bDec = round($b * 100);
$cDec = round($c * 100);

if ($aDec + $bDec == $cDec) {
    ...
}
...