当 PHP 精度设置为 18+ 时,round() 会中断


round() breaks when PHP precision is set to 18+

当我将 PHP 的precision设置设置为 18 或更高的值(无论是在 php.ini 中还是在运行时(时,round()函数会产生意想不到的结果。这是一个错误吗?或者我错过了什么?

例如,将浮点数12.4886724321舍入为小数精度4结果如下:

14: 12.4887
...
17: 12.4887
18: 12.4886999999999997
19: 12.48869999999999969
20: 12.48869999999999969
21: 12.4886999999999996902
22: 12.4886999999999996902
23: 12.488699999999999690203
24: 12.4886999999999996902034

测试用例如下:

$floaty = 12.4886724321;
for ($i = 14; $i <= 24; $i++) {
    ini_set('precision', $i);
    echo ini_get('precision') . ': ';
    echo round($floaty, 4)  . "'n";
}

即使我将$floaty设置为仅12.4,我也会得到"外推"小数的18: 12.4800000000000004等。我们以精度18或更高的精度进入的暮光之城究竟是什么?我确实知道如何清理输出,但我想知道为什么会发生这种情况,以及它是否是预期的行为。

在 PHP 7.0.2 @ W7x64 和 PHP 5.6.8 @ CentOS 6.5 上测试,结果相同。 number_format()没有这样做,假设它在如何截断小数方面更直截了当(或非数学(。

编辑:似乎所有数学操作都会发生? 1234.56 - 1233.03以不同的精度进行迭代:

12: 1.53
13: 1.53
14: 1.53
15: 1.52999999999997
16: 1.529999999999973
17: 1.5299999999999727
18: 1.52999999999997272

以 10 为底的浮点数(如 0.1 或 0.7(在以 2 为底的浮点数中没有精确表示

见 http://floating-point-gui.de

PHP 文档浮动

请参阅浮点精度浮点数的精度有限。虽然它取决于系统,但PHP通常使用IEEE 754双精度格式,由于舍入为1.11e-16,这将产生最大的相对误差。非初等算术运算可能会产生较大的误差,当然,当多个运算复合时,必须考虑误差传播。

此外,在以

10 为底的浮点数(如 0.1 或 0.7(中完全表示为有理数,在以 2 为底的浮点数时没有精确表示,无论尾数的大小如何,浮点数在内部使用。因此,它们不能在不损失精度的情况下转换为其内部二进制对应项。这可能会导致混淆的结果:例如,floor((0.1+0.7(*10( 通常会返回 7 而不是预期的 8,因为内部表示将类似于

7.99999999999999999991118....

所以永远不要相信浮点数结果到最后一位,也不要直接比较浮点数的相等性。如果需要更高的精度,可以使用任意精度的数学函数和 gmp 函数。