为了账号安全,请及时绑定邮箱和手机立即绑定

查找浮点错误的现实示例

查找浮点错误的现实示例

PHP
犯罪嫌疑人X 2023-09-08 21:46:05
处理金钱和其他精度至关重要的小数的通常建议是使用整数或字符串(加上任意精度库),如果您了解浮点数学的工作原理,那么这是有意义的。然而,我手头没有任何具体的例子来说明这一点,因为我在野外发现的每个错误计算都是由于其他一些错误造成的:使用 进行天真比较,显示结果时缺乏适当的舍入,公然错误的逻辑==(例如,使用不一致的算法计算税收,该算法在纸上也不起作用)...我做了一些研究,结果要么仅适用于 C/C++(具有不同精度的浮点/双精度),要么只是阐述为什么可以不相信两个浮点数相等。您能否分享一个独立的 PHP 代码片段,其中包含精心挑选的浮点数字和正确的算法,该算法会呈现由浮点限制明确导致的错误结果?免责声明:我无意争论、反驳或揭穿任何东西,老实说我需要一个例子来作为我的工具带。
查看完整描述

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)

上述两个例子都表明,实际上,使用浮点数进行(特别是)货币算术可能会出现问题。由于结果不再符合您的期望,因此它不仅可能导致明显错误的结果,而且细微的不同结果可能使整个应用程序以意想不到的方式运行。



查看完整回答
反对 回复 2023-09-08
?
MYYA

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 分钟),因此它并不像我想象的那样真实喜欢,但这确实说明了根本问题。


查看完整回答
反对 回复 2023-09-08
?
不负相思意

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 上测试


查看完整回答
反对 回复 2023-09-08
  • 3 回答
  • 0 关注
  • 122 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信