<2011 11 10> 编程中数据处理的问题(三)浮点数运算与精度误差

上篇博文提到了两种误差,“转换误差”和“舍入误差”。其实前者完全是因为我们只习惯用十进制的科学计数法,而计算机暂时又只能使用二进制,所以必须转换进制实现人与机器的数据互动。有要是有种外星人天生习惯用二进制思考问题,我想就没这个问题啦。Ha, joking!
这篇博文进一步探讨一下,机器具体怎么在运算中产生“舍入误差”,以及这种误差对数值计算算法有什么样的影响。首先是 机器精度Em,这个概念很基础,但是时间长了容易模糊,只知道IEEE设计浮点数时使机器表示实数具有离散化的特性。有一种直观的理解方法就是:两个实数的差值小于 “它们平均值的E m倍”的时候,这两个数在机器中就作为同一个数存储。   这种表述可以和浮点数的数轴标识对应起来。
If abs(A - B) < (A + B)/2 * E m 
Then A == B in MACHINE;
一般在可以用这样的算法得到机器精度epsilon:
epsilon = 1;
loop = 0;
maxloop = 100;
while loop < maxloop
   epsilon = epsilon/2;
   b = 1 + epsilon;
   if b == 1
      break;
   end
   loop = loop + 1;
end
 
如果实数的运算结果在机器精度表示的范围之外,那么就产生了舍入误差。
有两种情况舍入误差会造成较大的影响。一种是误差的累积,在许多(如几百万次)步骤运算后,微小的误差可能会累积产生对结果有明显的影响。另一种比较有趣,即便的步骤不多的运算,也有可能产生灾难性的误差。比如计算自然底数公式:
e = Lim n->∞(1 + 1/n) n
在计算时可以选择一个较大的数得到一个e的近似值。用一个较大的值n计算得到的近似值 f(n)和标准e值2.718281828459046 比较,得到误差error,无论用什么语言实现,结果都是类似的。下面是用8Bytes的float实现得到的数据(Intel & Windows平台):
    n                f(n)                     error
 1.0e+000    2.0000000000    0.7182818285
 1.0e+002    2.7048138294    0.0134679990
 1.0e+004    2.7181459268    0.0001359016
 1.0e+006    2.7182804691    0.0000013594
 1.0e+008    2.7182817983    0.0000000301
 1.0e+010    2.7182820532    0.0000002248
 1.0e+012    2.7185234960    0.0002416676
 1.0e+014    2.7161100341    0.0021717944
 1.0e+016    1.0000000000    1.7182818285
 
可以看出n在10 8的时候能到的最佳的近似值,而往后到10 16的时候,误差竟然突增到灾难性的地步。虽然公式 e = (1 + 1/n) 只有几步的计算,却产生了与理想情况巨大的误差。这是因为“1 + 1/n”的浮点加法在两个数相差很大的数量级的时候有很大的精度损失。8Bytes的浮点数拥有16位的十进制有效数字,因此当n=10 16 时,达到了有效数字的极限,1/n和1相比直接可以被忽略掉,结果自然也就是 1 了。
因此,浮点数运算有这样的特点“当 两个相差好几个数量级的浮点数相加 或者 两个几乎相等的浮点数相减时,舍入误差达到最大”。
 
另外在科学计算中经常被提起的误差来源于“用离散逼近连续函数”导致的截断误差(Truncation error),但是这种误差与机器本身和机器语言并没有关系,是属于算法设计中的问题。
 
研究机器运算的误差特点有两个用处,一个是在进行科学计算算法设计时能够有意识的想到这些问题,遇到问题也能够合理的分析;二是帮助我们更加熟悉面对的机器的特性,也是很有趣的过程。

你可能感兴趣的:(浮点数)