减法时PHP浮点计算错误

标签 php math floating-point formatting

我有一个非常奇怪的问题。如果我减去 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/1000.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/

相关文章:

objective-c - 需要一个 float 以标准形式 Obj C 显示

PHP 意外包含打印

c++ - 在这种情况下, 'addition' 和 'bitwise or' 是否相同?

algorithm - 我如何知道多边形的内部是位于顶点的右侧还是左侧?

iphone - 如何将进度条从 0.0 更新为 1.0 值?

assembly - 使用按位运算将 Int 转换为 Float 或 Float 为 Int(软件浮点)

floating-point - 在序言中将 float 转换为整数

php - 动态添加的表单字段 - pdo php mysql

php - 尝试在 ec2 上安装 php-mbstring 的依赖性问题

php - 将字符串分解为变量