简体   繁体   English

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

[英]C++ How to avoid floating-point arithmetic error

I am writing a loop that increments with a float, but I have come across a floating-point arithmetic issue illustrated in the following example: 我正在编写一个使用float递增的循环,但我遇到了以下示例中说明的浮点算术问题:

for(float value = -2.0; value <= 2.0; value += 0.2)
    std::cout << value << std::endl;

Here is the output: 这是输出:

-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

Why exactly am I getting 1.46031e-07 instead of 0 ? 为什么我得到1.46031e-07而不是0 I know this has something to do with floating-point errors, but I can't grasp why it is happening and what I should do to prevent this from happening (if there is a way). 我知道这与浮点错误有关,但是我无法理解它为什么会发生以及我应该做些什么来防止这种情况发生(如果有办法)。 Can someone explain (or point me to a link) that will help me understand? 有人可以解释(或指向我的链接),这将有助于我理解? Any input is appreciated. 任何输入都表示赞赏。 Thanks! 谢谢!

As everybody else has said, this is do to the fact that the real numbers are an infinite and uncountable set, while floating point representations use a finite number of bits. 正如其他人所说的那样,实际数字是无限且不可数的集合,而浮点表示使用有限数量的位。 Floating point numbers can only approximate real numbers and even in many simple cases are not precise, due to their definition. 浮点数只能逼近实数,即使在很多简单的情况下由于它们的定义也不精确。 As you have now seen, 0.2 is not actually 0.2 but is instead a number very close to it. 正如您现在所看到的, 0.2实际上不是0.2而是非常接近它的数字。 As you add these to value , you accumulate the error at each step. 当您将这些value添加到value ,您会在每一步累积错误。

As an alternative, try using int s for your iteration and dividing the result to get it back in the domain you require: 作为替代方案,尝试使用int s进行迭代并将结果除以将其返回到您需要的域中:

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

For me this gives: 对我来说,这给了:

-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

This is because floating point numbers have only a certain discrete precision. 这是因为浮点数只有一定的离散精度。

The 0.2 is not really a 0.2, but is internally represented as a slightly different number. 0.2实际上不是0.2,但在内部表示为略微不同的数字。

That is why you are seeing a difference. 这就是你看到差异的原因。

This is common in all floating point calculations, and you really can't avoid it. 这在所有浮点计算中都很常见,你真的无法避免它。

There's no clear-cut solution for avoid floating point precision loss. 没有明确的解决方案可以避免浮点精度损失。 I would suggest having a look through the following paper: What every computer scientist should know about floating point arithmetic . 我建议看看下面的文章: 每个计算机科学家应该了解浮点运算

Let's do your loop, but with increased output precision. 让我们做你的循环,但增加输出精度。

code: 码:

for(float value = -2.0; value <= 2.0; value += 0.2)
    std::cout << std::setprecision(100) << value << std::endl;

output: 输出:

-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

Use integers and divide down: 使用整数并分解:

for(int value = -20; value <= 20; value += 2)
    std::cout << (value/10.0) << std::endl;

Learn about floating point representation with some Algorithms book or using internet. 通过一些算法书或使用互联网了解浮点表示。 There are lots of resources out there. 那里有很多资源。

For the time, what you want seems to be some way to get zero when its something very very close to zero. 当时,当你的东西非常接近于零时,你想要的东西似乎是某种方式来获得零。 and we all know that we call this process "rounding". 我们都知道我们称这个过程为“四舍五入”。 :) so why don't you use it while printing those numbers. :)所以为什么不在打印这些数字时使用它。 printf function provides good formatting power for these kinds of things. printf函数为这些东西提供了良好的格式化功能。 check the tables in the following link if you dont know how to format with printf. 如果您不知道如何使用printf格式化,请检查以下链接中的表格。 ( you can use the formating for rounding and displaying the numbers correctly ) printf ref : http://www.cplusplus.com/reference/cstdio/printf/?kw=printf (您可以使用格式化来正确舍入和显示数字)printf ref: http ://www.cplusplus.com/reference/cstdio/printf/?kw = printf

-- edit -- - 编辑 -

maybe some of you know know that according to mathematics 1.99999999.... is the same as 2.0 . 也许有些人知道根据数学1.99999999 ....就像2.0一样。 Only difference is the representation. 唯一的区别是代表性。 But the number is the same. 但数量是一样的。

your floating point problem is a little bit similar to this. 你的浮点问题有点类似于此。 ( this is just for your clarification only. your problem is not the same as the 1.9999.... thing. ) (这只是为了您的澄清。您的问题与1.9999 ....不一样。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM