0

对于此代码:

$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 -向下舍入分数

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

round(200.1 / 0.1, PHP_ROUND_HALF_DOWN)

但 round 按预期返回 2001,而 floor 返回 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。

4

1 回答 1

1

您遇到了精度问题。你不应该相信浮点数到 PHP 中的最后一个数字。

从手册(浮点数)中,第二段中甚至还有一点关于您的特定测试数字:

浮点数的精度有限。尽管取决于系统,但 PHP 通常使用 IEEE 754 双精度格式,由于在 1.11e-16 的数量级四舍五入,这将给出最大的相对误差。非初等算术运算可能会产生较大的误差,当然,当多个运算复合时,必须考虑误差传播。

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

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

对于“简单”的解释,请参阅同样标题为“为什么我的数字不加起来?”的浮点指南。

如果您需要这种精度,请尝试使用BC Mathgmp函数。

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

于 2020-07-31T02:15:38.063 回答