php fmod 返回不可用的结果

php fmod returning unusable result

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() 的 return 值时,该方法就会失败。 现实生活中如何不用自定义代码轻松查看价格区间?

这个或类似的问题一直存在,但我能找到的只是解释为什么 fmod() 的行为如此。从来没有答案如何解决这个现实生活中的问题...

这里的问题是 314.6 正好是 1.3 * 242,所以浮点数余数为零,但由于您很清楚 IEEE 754 的不准确性,您得到 0.00000000000001199041

但是不要忘记浮点数学的规则之一:你不能粗心地 compare floats 求相等。如果您的参数有一位小数,则您的结果不需要 14 位精度。你有价格:多少小数点在你的货币中有意义?如果您正在使用例如欧元你不太可能使用超过两个(美分)并且 1.1990408665952E-14 对所有效果都是零:

var_dump(round(1.1990408665952E-14, 2));

double(0)

有些人建议使用整数(例如美分而不是欧元)进行所有价格计算以避免舍入错误,但大多数现实生活中的问题都来自非常具体的错误:

  • 向用户显示原始浮点数(而不是对其进行舍入和格式化)。
  • 进行原始比较(if ($foo == $bar))。

… 无论如何,整数并不能防止所有舍入错误(例如,个别项目的税收计算与发票总额的计算不匹配)。