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()
ведет себя как это делает. Никогда не отвечайте, как решить эту реальную проблему …
Проблема здесь в том, что 314.6
это точно 1.3 * 242
так что остаток с плавающей точкой равен нулю, но вы получите 0.00000000000001199041
из-за неточностей IEEE 754, о которых вы хорошо знаете.
Но не забывайте одно из правил математики с плавающей точкой: вы не можете сравнить поплавки за равенство небрежно. Если ваши аргументы имеют одну десятичную позицию, вам не нужна точность в 14 позиций. У вас есть цены: сколько десятичных знаков имеет смысл в вашей валюте? Если вы использовали, например, евро, вы вряд ли будете использовать более двух (центов) и 1.1990408665952E-14
ноль для всех эффектов:
var_dump(round(1.1990408665952E-14, 2));
двойная (0)
Некоторые люди рекомендуют делать все математические расчеты с использованием целых чисел (например, центов вместо евро), чтобы избежать ошибок округления, но большинство реальных проблем возникают из-за очень специфических ошибок:
if ($foo == $bar)
).… И целые числа не предотвращают все ошибки округления в любом случае (например, расчеты налога по отдельным позициям, не совпадающие с расчетами по итоговым суммам счетов).
Других решений пока нет …