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

时间:2016-01-31 07:10:51

标签: php math rounding precision

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

结果例如将float 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

1 个答案:

答案 0 :(得分:1)

基数10中的浮点数,如0.1或0.7,没有精确表示为基数2中的浮点数

请参阅http://floating-point-gui.de

PHP文档要浮动

Floating point precision 浮点数的精度有限。虽然它取决于系统,但PHP通常使用IEEE 754双精度格式,由于舍入的顺序为1.11e-16,因此会产生最大的相对误差。非基本算术运算可能会产生更大的误差,当然,当复合多个运算时,必须考虑误差传播。

此外,基本10中的浮点数精确表示的有理数,如0.1或0.7,没有精确表示为基数2中的浮点数,无论尾数的大小如何,它都在内部使用。因此,它们不能在没有很小精度损失的情况下转换为它们的内部二进制对应物。这可能会导致令人困惑的结果:例如,floor((0.1 + 0.7)* 10)通常会返回7而不是预期的8,因为内部表示将类似于7.9999999999999991118 ....

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