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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14859875/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:45:18  来源:igfitidea点击:

C++ How to avoid floating-point arithmetic error

c++floating-pointfloating-point-precision

提问by Barney

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:

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

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-07instead of 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!

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

采纳答案by Srikant Krishna

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.

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

回答by Joseph Mansfield

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.2is not actually 0.2but is instead a number very close to it. As you add these to value, you accumulate the error at each step.

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

As an alternative, try using ints for your iteration and dividing the result to get it back in the domain you require:

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

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

回答by bstamour

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.

没有明确的解决方案可以避免浮点精度损失。我建议阅读以下论文:每个计算机科学家都应该了解的关于浮点运算的知识

回答by Bill Lynch

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

回答by QuentinUK

Use integers and divide down:

使用整数并除以:

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

回答by Deamonpog

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. printffunction provides good formatting power for these kinds of things. check the tables in the following link if you dont know how to format with 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函数为这些类型的事物提供了良好的格式化能力。如果您不知道如何使用 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 . Only difference is the representation. But the number is the same.

也许你们中的一些人知道,根据数学 1.99999999.... 与 2.0 相同。唯一的区别是表示法。但是数量是一样的。

your floating point problem is a little bitsimilar to this. ( this is just for your clarification only. your problem is not the same as the 1.9999.... thing. )

您的浮点问题与此有点相似。(这只是为了您的澄清。您的问题与 1.9999 .... 的事情不同。)