当我将 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 函数。