3 回答
TA贡献1811条经验 获得超4个赞
只要迭代次数少于十亿次,事情就会很容易崩溃。问题是,通过使用浮点数和算术,您可以很容易地发现自己得到意想不到的结果,即使数字表面看起来不错,细微的不精确性也可能导致应用程序出错。
让我们尝试一下答案中示例的变体:
$total = 0.0;
for ($i = 0; $i < 10; $i++) {
$total += 0.1;
}
echo "added ten cents, ten times\n";
// since we added 0.1 € x 10 times, we now have 1€ in total, right?
if ($total == 1) {
echo "I have 1€. All is good in the realm.";
}
else {
echo "WTF? Where is my money? I only have {$total}€!!!!\n";
echo "\$total holds: ";
var_dump($total);
}
上述的输出是:
added ten cents
WTF? Where is my money? I only have 1€!!!!
$total holds: float(1)
即使$total看起来是这样float(1),代码也会遵循“错误”的执行分支,从而破坏了我们的应用程序。
如果我们在 PHP8(目前为测试版)中执行相同的代码,您将得到更容易理解的结果:
added ten cents
WTF? Where is my money? I only have 1€!!!!
$total holds: float(0.9999999999999999)
另一个简单的例子:
$balance = 50.03;
$debit = 45.42;
$expected_balance = 4.61;
$real_balance = $balance - $debit;
if ($real_balance !== $expected_balance) {
echo "problems: ";
var_dump($real_balance);
}
上述的输出是:
balance mismatch: float(4.61)
或者,在 PHP8 中:
balance mismatch: float(4.609999999999999)
上述两个例子都表明,实际上,使用浮点数进行(特别是)货币算术可能会出现问题。由于结果不再符合您的期望,因此它不仅可能导致明显错误的结果,而且细微的不同结果可能使整个应用程序以意想不到的方式运行。
TA贡献1868条经验 获得超4个赞
这个问题没有什么意义,因为这不是浮点错误的工作原理。
误差很小。它们以远程小数形式出现,只有当您需要非常高的精度水平时它们才会被注意到。毕竟,IEEE 754 为绝大多数计算机系统提供支持,并且它提供了出色的精度。具体来说,0.1 公里以浮点数表示为 0.100000001490116119384765625,如果我没有算错的话,精确度可达 1/10 µm。
可能没有一组精心挑选的数字和现实生活中的计算需要您使用 PHP 进行(发票、证券交易所指数...),无论您多么小心地精确,都会呈现不正确的结果水平。因为那不是问题。
浮点数学的问题在于,它迫使您在每一步上都非常小心,并且很容易出现错误。
对于精度很重要的应用程序,您可以使用浮点数编写正确的软件,但它不会那么简单、可维护或健壮。
原答案:
这是迄今为止我得到的最好的:
// Set-up and display settings (shouldn't affect internal calculations or final result)
set_time_limit(0);
ini_set('precision', -1);
// Expected accuracy: 2 decimal positions
$total = 0;
for ($i = 0; $i < 1e9; $i++) {
$total += 0.01;
// It's important to NOT round inside the loop, e.g.: $total = round($total + 0.01, 2);
}
var_dump($total, number_format($total, 2));
float(9999999.825158669)
string(12) "9,999,999.83" // Correct value would be "10,000,000.00"
不幸的是,它依赖于大量精度误差的累积(它需要大约 1,000,000,000 个误差才会发生,并且在我的 PC 上运行需要超过 4 分钟),因此它并不像我想象的那样真实喜欢,但这确实说明了根本问题。
TA贡献1777条经验 获得超10个赞
echo floor((0.1 + 0.7) * 10);
预期结果:0.1+0.7=0.8;0.8*10=8;
结果:7
在 PHP 7.2.12 上测试
- 3 回答
- 0 关注
- 122 浏览
添加回答
举报