C++ 数值截断误差

7

抱歉,如果问题比较简单,我找不到答案。

#include <iostream>

using namespace std;

int main()
{
double a(0);
double b(0.001);
cout << a - 0.0 << endl;
for (;a<1.0;a+=b);
cout << a - 1.0 << endl;
for (;a<10.0;a+=b);
cout << a - 10.0 << endl;
cout << a - 10.0-b << endl;
return 0;
}

输出结果:
0
6.66134e-16
0.001
-1.03583e-13

尝试使用MSVC9、MSVC10、Borland C++ 2010编译,最终都会出现约为1e-13的错误。在只有1000或10000个增量的情况下,出现如此显著的误差积累是否正常?


3
http://docs.sun.com/source/806-3568/ncg_goldberg.html - Anycorn
http://home.comcast.net/~tom_forsyth/blog.wiki.html#[[A%20matter%20of%20precision]]的翻译如下:(不是我,嘿) - dash-tom-bang
4个回答

13

谢谢,我确实理解截断误差和其他一切烦人的东西。我在数值方法方面做了很多理论工作,但我从未自己检查过,发现它有多大,感到非常惊讶... - Andrew
1
一个double类型大约可以提供16位小数的精度,这是正确的。当你循环1000次时,你只剩下13位左右的精度。 - WhirlWind
1
请注意,仅针对无理(在二进制中)数字会特别痛苦。如果您使用2的幂,那么一切都会很好。例如,如果您的加法器是0.5、0.25、0.125、0.0625等,它们最终将完全相加为1.0,因为这些值在二进制中分别为0.1、0.01、0.001和0.0001。 - dash-tom-bang
1
@dash-tom-bang 不错的观点,不过我认为如果你在做那种事情,最好还是适应整数算术。 - WhirlWind
@ WhirlWind:在我内心深处,我曾经期望它由于误差性质的随机性而缓慢积累,但现在我意识到这只是一种幻想 :) - Andrew
如果你有一个固定数量的小数位,整数总是更好的选择。例如,不要用浮点数计算美元,而是用表示为整数,其中1代表一分钱或者几分钱。 - dash-tom-bang

2
这就是为什么在使用浮点数时,你永远不应该这样做:

if( foo == 0.0 ){
    //code here
}

而不是这样做

bool checkFloat(float _input, float _compare, float _epsilon){
    return ( _input + _epsilon > _compare ) && ( _input - _epsilon < _compare );
}

1
在许多情况下,检查是否为零是可以的,因为它可以在硬件中精确表示,但当然这取决于您对其进行的操作... 从1开始并减去0.1十次将不会导致0,当然。 - dash-tom-bang
1
这非常正确,但在大多数情况下,这并不是一个好的做法。我更喜欢谨慎行事。 - wheaties
在谨慎的一面犯错...有点有趣 ;) 或者说是在 epsilon 的范围内犯错。 - WhirlWind

2
想象一下,每次操作都会引入轻微的误差,但是下一次操作使用了略有缺陷的结果。经过足够多的迭代,你会偏离真实结果。如果你愿意,可以按照如下形式编写表达式并找出具有 epsilon 的术语。根据这些术语,您可以估计近似误差。
还有第二个误差来源:在某些时候,您将组合相对较小和相对较大的数字,接近最后阶段。如果您回忆一下机器精度的定义,1 + e = 1,那么在某些时候,操作将丢失重要位数。
希望这能以通俗易懂的方式阐明问题。

1
这是浮点数的问题——它们是近似值,并且在零点发生奇怪的情况(也就是,会出现奇怪的表示形式)。因此,你对数字进行的一些操作必须更加谨慎地处理。
当比较两个数字时,你不能简单地说 a == b,因为其中一个可能是 0,而另一个由于在得到 a 和 b 时施加了浮点运算而变成 -1.03583e-13。你必须选择一个任意的容差,像这样:fabs(a,b) < 1e-8。
当打印一个数字时,通常需要限制打印的位数。如果你使用 printf,可以使用 printf("%g\n", a);,它不会打印像 -1.03583e-13 这样的东西。我不知道是否有一个 iostream 的类似于 %g 的函数?有吗?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接