当PHP精度设置为18+时,round()中断 [英] round() breaks when PHP precision is set to 18+

查看:124
本文介绍了当PHP精度设置为18+时,round()中断的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我将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.9999999999999991118....

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

When I set PHP's precision setting to values 18 or higher (whether in php.ini or at runtime), the round() function yields unexpected results. Is this a bug; or what am I missing?

Results for e.g. rounding the float 12.4886724321 to 4 decimal precision are as follows:

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

Test case as follows:

$floaty = 12.4886724321;

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

Even if I set my $floaty to just 12.4, I'll get 18: 12.4800000000000004 etc. for "extrapolated" decimals. What exactly is this twilight zone we're entering at precisions 18 or greater? I do know how to clean things up for output, but I'd like to know why this happens, and whether it's intended behavior.

Tested on PHP 7.0.2 @ W7x64 and PHP 5.6.8 @ CentOS 6.5 with identical results. number_format() doesn't do this, suppose it's more blunt (or non-mathematical) in how it truncates the decimals.

EDIT: Seems to happen with all math ops? 1234.56 - 1233.03 iterated at different precisions:

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

解决方案

Floats in base 10, like 0.1 or 0.7, do not have an exact representation as float numbers in base 2

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

PHP Docs to float

see Floating point precision Floating point numbers have limited precision. Although it depends on the system, PHP typically uses the IEEE 754 double precision format, which will give a maximum relative error due to rounding in the order of 1.11e-16. Non elementary arithmetic operations may give larger errors, and, of course, error propagation must be considered when several operations are compounded.

Additionally, rational numbers that are exactly representable as floating point numbers in base 10, like 0.1 or 0.7, do not have an exact representation as floating point numbers in base 2, which is used internally, no matter the size of the mantissa. Hence, they cannot be converted into their internal binary counterparts without a small loss of precision. This can lead to confusing results: for example, floor((0.1+0.7)*10) will usually return 7 instead of the expected 8, since the internal representation will be something like 7.9999999999999991118....

So never trust floating number results to the last digit, and do not compare floating point numbers directly for equality. If higher precision is necessary, the arbitrary precision math functions and gmp functions are available.

这篇关于当PHP精度设置为18+时,round()中断的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆