PHP 不一致 var_dump,浮动计算的楼层结果错误

PHP inconsistent var_dump, wrong floor result with a float calulation

对于此代码:

$value = 200.1;
$denominator = 0.1;
echo "value: $value\n";
echo "denominator: $denominator\n";

$resultInt = ($value / $denominator);
echo "($value / $denominator) = ";
printf ("%f\n", $resultInt);

$resultInt = (int) ($value / $denominator);
echo "(int) ($value / $denominator) = ";
printf ("%f\n", $resultInt);

$resultInt = floor($value / $denominator);
echo "floor($value / $denominator) = ";
printf ("%f\n", $resultInt);

$resultInt = floor((int) ($value / $denominator));
echo "floor((int) ($value / $denominator)) = ";
printf ("%f\n", $resultInt);

$resultInt = floor((float) 2001);
echo "floor((float) 2001) = ";
printf ("%f\n", $resultInt);

$resultInt = round($value / $denominator, PHP_ROUND_HALF_DOWN);
echo "round($value / $denominator, PHP_ROUND_HALF_DOWN) = ";
printf ("%f\n", $resultInt);

$valueMul = $resultInt * $denominator;
if ($valueMul !== $value) {
    echo "they are not the same\n";
    var_dump($value);
    var_dump($valueMul);
}

$valueDiff = $value - $valueMul;
if ($valueDiff !== 0) {
    echo "valueDiff is not zero\n";
    var_dump($valueDiff);
}

我有这个结果:

value: 200.1
denominator: 0.1
(200.1 / 0.1) = 2001.000000
(int) (200.1 / 0.1) = 2000.000000
floor(200.1 / 0.1) = 2000.000000
floor((int) (200.1 / 0.1)) = 2000.000000
floor((float) 2001) = 2001.000000
round(200.1 / 0.1, PHP_ROUND_HALF_DOWN) = 2001.000000
they are not the same
float(200.1)
float(200.1)
valueDiff is not zero
float(-2.8421709430404E-14)

以上所有计算的预期结果是 2001,但在某些情况下是 2000。

我知道由于计算机在二进制文件上运行,分数没有作为小数存储在变量中。

但正如 floor 的手册所述:

floor — Round fractions down

因此我希望得到与以下相同的结果:

round(200.1 / 0.1, PHP_ROUND_HALF_DOWN)

但如预期的那样 returns 2001 年和下限 returns 2000 年(错误)。

你知道为什么吗?

我也看到var_debug不一致:

代码:

$resultInt = round($value / $denominator, PHP_ROUND_HALF_DOWN);
echo "round($value / $denominator, PHP_ROUND_HALF_DOWN) = ";
printf ("%f\n", $resultInt);

$valueMul = $resultInt * $denominator;
if ($valueMul !== $value) {
    echo "they are not the same\n";
    var_dump($value);
    var_dump($valueMul);
}

它将两个值显示为相同(但它们不是):

they are not the same
float(200.1)
float(200.1)

但对于它们的差异

$valueDiff = $value - $valueMul;
if ($valueDiff !== 0) {
    echo "valueDiff is not zero\n";
    var_dump($valueDiff);
}

它打印非零值

因此,在一种情况下,它会将两个值打印为相同,但从另一个中减去一个的结果不会打印为零 - 为什么?

在 PHP 中可以使用什么函数来始终看到浮点数或整数的真实值,而不是其复杂的十进制表示形式? 我的意思是输出:

they are not the same
float(200.1)
float(200.1)

我应该看到不同的值,不一样。

以一致的方式打印浮点数的十进制表示的函数是什么? 我的意思是如果在示例中

they are not the same
float(200.1)
float(200.1)

浮点数的打印完全相同(即使实际上并非如此)然后在示例中:

valueDiff is not zero
float(-2.8421709430404E-14)

相同的函数应该因此打印 0。

您 运行 陷入精度问题。你不应该相信浮点数到 PHP.

中的最后一位

根据手册(Floating Point Numbers),在第二段中甚至还有关于您的特定测试编号的要点:

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.

For a "simple" explanation, see the » floating point guide that's also titled "Why don’t my numbers add up?"

如果您需要那种精度,请尝试使用 BC Math or gmp 函数。

这里是手册中引用的浮点指南的相关页面:https://floating-point-gui.de/languages/php/