C++如何避免浮点算术错误

Bar*_*ney 8 c++ floating-point floating-point-precision

我正在编写一个使用float递增的循环,但是我遇到了以下示例中说明的浮点算术问题:

for(float value = -2.0; value <= 2.0; value += 0.2)
    std::cout << value << std::endl;
Run Code Online (Sandbox Code Playgroud)

这是输出:

-2
-1.8
-1.6
-1.4
-1.2
-1
-0.8
-0.6
-0.4
-0.2
1.46031e-07
0.2
0.4
0.6
0.8
1
1.2
1.4
1.6
1.8
Run Code Online (Sandbox Code Playgroud)

我到底为什么要1.46031e-07而不是0?我知道这与浮点错误有关,但是我无法理解它为什么会发生以及我应该做些什么来防止这种情况发生(如果有办法).有人可以解释(或指向我的链接),这将有助于我理解?任何输入都表示赞赏.谢谢!

Jos*_*eld 28

正如其他人所说的那样,实际数字是无限且不可数的集合,而浮点表示使用有限数量的位.浮点数只能逼近实数,即使在很多简单的情况下由于它们的定义也不精确.正如你现在所看到的那样,0.2实际上0.2并不是一个非常接近它的数字.在添加这些内容时value,您会在每一步累积错误.

作为替代方案,尝试使用ints进行迭代并将结果除以将其返回到您需要的域中:

for (int value = -20; value <= 20; value += 2) {
  std::cout << (value / 10.f) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

对我来说,这给了:

-2
-1.8
-1.6
-1.4
-1.2
-1
-0.8
-0.6
-0.4
-0.2
0
0.2
0.4
0.6
0.8
1
1.2
1.4
1.6
1.8
2
Run Code Online (Sandbox Code Playgroud)

  • 我不敢相信这不是公认的答案,因为这个和另外一个答案是唯一能给出解决方案的答案. (2认同)

小智 7

这是因为浮点数只有一定的离散精度.

0.2实际上不是0.2,但在内部表示为略微不同的数字.

这就是你看到差异的原因.

这在所有浮点计算中都很常见,你真的无法避免它.

  • 重要的是要指出,虽然0.2不能精确地表示为浮点数,但是-2.0和2.0可以。我指出这一点只是为了避免给人以浮点数学是任意和反复无常的印象。发生的一切是浮点数和重复使用基数2,而0.2等于1/5,不能将其表示为有限的基数2。-2、2.0、0.5、0.25,-。375和178432都可以精确表示。 (2认同)

bst*_*our 7

没有明确的解决方案可以避免浮点精度损失.我建议看看下面的文章:每个计算机科学家应该了解浮点运算.

  • +1 用于指向本文的链接。对于那些想要 HTML 而不是 PDF 的人:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html (2认同)

Bil*_*nch 6

让我们做你的循环,但增加输出精度.

码:

for(float value = -2.0; value <= 2.0; value += 0.2)
    std::cout << std::setprecision(100) << value << std::endl;
Run Code Online (Sandbox Code Playgroud)

输出:

-2
-1.7999999523162841796875
-1.599999904632568359375
-1.3999998569488525390625
-1.19999980926513671875
-0.999999821186065673828125
-0.79999983310699462890625
-0.599999845027923583984375
-0.3999998569488525390625
-0.19999985396862030029296875
1.460313825418779742904007434844970703125e-07
0.20000015199184417724609375
0.400000154972076416015625
0.6000001430511474609375
0.800000131130218505859375
1.00000011920928955078125
1.20000016689300537109375
1.40000021457672119140625
1.60000026226043701171875
1.80000030994415283203125
Run Code Online (Sandbox Code Playgroud)


Que*_*nUK 5

使用整数并分解:

for(int value = -20; value <= 20; value += 2)
    std::cout << (value/10.0) << std::endl;
Run Code Online (Sandbox Code Playgroud)