php fmod возвращает непригодный результат
fmod(floatval("314.6"), floatval("1.3"))
=> 1.1990408665952E-14
Я более или менее понимаю основную проблему представления чисел в двоичной форме и что-то с IEEE-754. Но: Как бы я получил результат, который можно использовать в практике?
Пример того, чего я хочу достичь: Продавец может определить цену P своего товара, но цена должна быть кратна x:
if (fmod(P, x) != 0) { echo "price must be a multiple of " . x; }
Это было бы так просто, но подход терпит неудачу всякий раз, когда я получаю что-то вроде 1234E-18 в качестве возвращаемого значения fmod()
.
Что делать в реальной жизни, чтобы легко проверить ценовой интервал без использования пользовательского кода?
Этот или подобные вопросы уже были, но все, что я могу найти, - это объяснения, почему fmod()
ведет себя так, как он. Никогда не будет ответа, как решить эту реальную проблему...
1 answers
Проблема здесь в том, что 314.6
точно равно 1.3 * 242
, поэтому остаток с плавающей запятой равен нулю, но вы получаете 0.00000000000001199041
из-за неточностей IEEE 754, о которых вы хорошо знаете.
Но не забывайте одно из правил математики с плавающей запятой: вы не можете сравнивать поплавки для равенства небрежно. Если ваши аргументы имеют одну десятичную позицию, вам не нужна точность в 14 позиций в ваших результатах. У вас есть цены: сколько десятичных знаков имеет смысл в вашей валюте? Если вы использовали, например, евро, вы маловероятно использовать более двух (центов), и 1.1990408665952E-14
равно нулю для всех эффектов:
var_dump(round(1.1990408665952E-14, 2));
Двойной(0)
Некоторые люди рекомендуют выполнять всю математику цен, используя целые числа (например, центы вместо евро), чтобы избежать ошибок округления, но большинство реальных проблем возникают из-за очень специфических ошибок:
- Отображение необработанных поплавков для пользователя (вместо округления и форматирования их).
- Выполнение необработанных сравнений (
if ($foo == $bar)
).
...и целые числа не препятствуют округлению ошибки в любом случае (например, расчеты налогов по отдельным позициям, не совпадающие с расчетами по итоговым счетам-фактурам).