我有一个非常奇怪的问题。如果我减去 2 个浮点变量,其中一个是数学运算的结果,我会得到一个错误的值。
例子:
var_dump($remaining);
var_dump($this->hours_sub['personal']);
echo $remaining-$this->hours_sub['personal'];
这是输出:
float 5.4
float 1.4
5.3290705182008E-15
5.4-1.4 应该是 4 如果我将这两个值相加,结果是正确的。
我的错误在哪里? 这不可能是一个四舍五入的问题。
最佳答案
如果仍然有人在访问此页面时遇到类似的问题,即 float 减法会导致错误或奇怪的值。 下面我将更详细地解释这个问题。
它与 PHP 没有直接关系,也不是错误。 但是,每个程序员都应该意识到这个问题。
这个问题甚至在二十年前夺走了许多人的生命。
1991 年 2 月 25 日,MIM-104 爱国者导弹连中的一个不正确的浮点运算(称为舍入误差)阻止了它在沙特阿拉伯达兰拦截一枚飞毛腿导弹,造成 28 名士兵死亡,近 100 名军人受伤。美国陆军第 14 军需支队。
但是为什么会这样呢?
原因是浮点值表示有限的精度。所以,一个值可能 任何处理(切掉)后都没有相同的字符串表示。它也是 包括在脚本中直接写入浮点值 无需任何数学运算即可打印。
只是一个简单的例子:
$a = '36';
$b = '-35.99';
echo ($a + $b);
您希望它打印 0.01
,对吗?
但它会打印一个非常奇怪的答案,例如 0.009999999999998
与其他数字一样, float double 或 float 以 0 和 1 的字符串形式存储在内存中。 float 与整数的不同之处在于我们在查看 0 和 1 时如何解释它们。它们的存储方式有很多标准。
float 通常作为符号位、指数字段和有效数或尾数从左到右打包到计算机数据中......
由于空间不足,十进制数不能很好地用二进制表示。所以,你不能完全按照 0.3333333...
来表达 1/3
,对吧?为什么我们不能将 0.01
表示为二进制 float 也是出于同样的原因。 1/100
是 0.00000010100011110101110000.....
重复 10100011110101110000
。
如果 0.01
以二进制的 01000111101011100001010
的简化和系统截断形式保存,当它被翻译回十进制时,它将被读取为 0.0099999 ....
取决于系统(64 位计算机将提供比 32 位更好的精度)。在这种情况下,操作系统决定是否按照它看到的方式打印它,或者如何以更易于阅读的方式进行打印。因此,他们想要如何表示它取决于机器。但是可以通过不同的方法在语言级别进行保护。
如果你格式化结果使用
echo number_format(0.009999999999998, 2);
它将打印 0.01
.
这是因为在这种情况下,您指示应该如何读取它以及您需要多高的精度。
注意 number_format() 不是唯一的函数,可以使用其他一些函数和方法来告诉编程语言关于精度期望。
引用资料:
https://sdqweb.ipd.kit.edu/publications/pdfs/saglam2016a.pdf
https://en.wikipedia.org/wiki/Round-off_error
关于减法时PHP浮点计算错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17210787/