PHP错误或我错了吗?

时间:2018-11-29 14:35:31

标签: php

function dec2sat( $decimal )
{
    return $decimal * (10**8);
}

echo dec2sat(0.06999206); // result: 6999206 (float type)

var_dump(dec2sat(0.06999206) >= 6999206); // result: FALSE

每个人都可以向我解释为什么var_dump(dec2sat(0.06999206) >= 6999206);返回FALSE吗?

经过测试:PHP 7.2.12,PHP 7.1.22,PHP 5.6.38

3 个答案:

答案 0 :(得分:2)

您正在尝试比较float与integer。在那种情况下,PHP需要将float显式转换为整数。当您在下面运行代码时:

var_dump((int)dec2sat(0.06999206)); // 6999205

您将获得号码6999205,该号码显然少于6999206。

同一件事是当您尝试比较字符串与整数时。这将输出true:

var_dump('string' == 0); // true

这是您始终需要比较类型和值以避免这种行为的方式。下面的代码将输出true,这不是一个漂亮的解决方案,但是可以工作。还要尝试其他情况。

var_dump(round(dec2sat(0.06999206), 0) >= 6999206); // true
  

为什么会这样:

浮点数的精度有限。尽管它取决于系统,但PHP通常使用IEEE 754双精度格式,由于舍入为1.11e-16的顺序,它将提供最大的相对误差。非基本算术运算可能会产生较大的错误,并且,当然,在合并多个运算时必须考虑错误传播。

此外,在底数10中可精确表示为浮点数的有理数(如0.1或0.7)在底数2中不具有作为浮点数的精确表示,内部使用,无论尾数的大小如何。因此,如果不损失一点点精度,就不能将它们转换为内部二进制副本。这可能导致混乱的结果:例如,floor((0.1 + 0.7)* 10)通常将返回7而不是预期的8,因为内部表示形式将类似于7.9999999999999991118 ....

在我们的示例中,当您将所有隐藏的数字都删除到小数点后时,您将获得下一个结果:

var_dump(number_format(dec2sat(0.06999206), 30, '.', '')); // 6999205.999999999068677425384521484375
var_dump(number_format(dec2sat(0.06999206), 9, '.', '')); // 6999205.999999999
var_dump(number_format(dec2sat(0.06999206), 8, '.', '')); // 6999206.00000000
  

结论:

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

将您返回的值转换为整数,或者将其舍入到小数点后8位。

答案 1 :(得分:0)

function dec2sat($decimal)
{
    return $decimal * (10 ** 8) . '';
}

答案 2 :(得分:0)

我要说的第一件事是:非常感谢您花费时间为我的问题找到解决方案。我的问题的主要目标不是找出解决方案,我需要知道导致上述意外结果的原因,因为它有助于避免以后重复。
在查看了AleksandarRakić的答案之后,我对PHP中的float类型进行了一些搜索,现在,我想我已经找到了导致var_dump(dec2sat(0.06999206) >= 6999206);返回FALSE的原因。您可以在这里阅读:Warning: Floating point precision

我认为数字0.06999206并不是真的0.06999206,在内部存储时可能是0.069992059999...,而函数dec2sat(0.06999206)的结果是6999205.999...,这就是为什么AleksandarRakić的解决方案返回预期结果。

这是经过编辑的dec2sat()

function dec2sat( $decimal )
{
     return intval(round( $decimal * (10**8) )); // $decimal always has decimal value <= 8 digits
}

函数dec2sat()的主要目标是将floating point number转换为int以避免直接在我的应用程序中处理floating point number