避免精度损失的最佳算法?

时间:2009-02-02 03:39:56

标签: precision numerical-analysis

我收到的最近一项家庭作业要求我们采取表达方式,这些表达方式可能会在计算机中执行时造成精度损失,并改变它们以避免这种损失。

不幸的是,这样做的方向尚未明确。通过观察正在执行的各种示例,我知道有一些方法可以做到这一点:使用泰勒级数,如果涉及平方根则使用共轭,或者在减去两个分数时找到共同的分母。

然而,我很难注意到何时会发生精度损失。到目前为止,我唯一知道的是,当你减去两个接近相同的数字时,由于高阶数字很重要,你会失去精确度,并且你会从四舍五入中丢失这些数字。

我的问题是我应该寻找的其他常见情况,以及接近它们的“好”方法是什么?

例如,这是一个问题:

f(x) = tan(x) − sin(x)  when x ~ 0

在这三种选择中评估这一点的最佳和最差算法是什么:

(a) (1/ cos(x) − 1) sin(x),
(b) (x^3)/2
(c) tan(x)*(sin(x)^2)/(cos(x) + 1).

据我所知,当x接近于零时,tan(x)和sin(x)几乎相同。我不明白为解决这个问题,这些算法是如何或为何更好或更差。

4 个答案:

答案 0 :(得分:5)

通常使用的另一个经验法则是:当添加一长串数字时,开始从最接近零的数字添加并以最大数字结束。

解释为什么这是好的是abit棘手。当你为一个大数字添加小数字时,它们有可能被完全丢弃,因为它们小于当前大数字尾数中的最小数字。以这种情况为例:

a = 1,000,000;
do 100,000,000 time:
   a += 0.01;

如果0.01小于最低的尾数,那么循环什么都不做,最终结果是= = 1,000,000 但如果你这样做:

a = 0;
do 100,000,000 time:
   a += 0.01;
a += 1,000,000;

比较低的数字慢慢增长,你更有可能得到接近== 2,000,000的东西,这是正确的答案。
这是一个极端的例子,但我希望你能得到这个想法。

答案 1 :(得分:4)

当我还是本科生时,我不得不参加一个数字课程,而且非常痛苦。无论如何,IEEE 754是通常由现代CPU实现的浮点标准。理解它的基础知识很有用,因为这会让你对不该做的事情有很多直觉。对它的简化解释是,计算机将浮点数存储在类似于base-2科学记数法的东西中,其中指数和尾数具有固定数量的位(位)。这意味着数字的绝对值越大,它的表示就越不精确。对于IEEE 754中的32位浮点数,一半可能的位模式表示介于-1和1之间,即使数字高达约10 ^ 38也可用32位浮点数表示。对于大于2 ^ 24(大约1670万)的值,32位浮点数不能完全代表所有整数。

这对您来说意味着您通常希望避免以下情况:

  1. 当预期最终答案很小时,中间值会很大。
  2. 在大数字中添加/减去小数字。例如,如果您写了类似的内容:

    for(float index = 17000000; index< 17000001; index ++){}

  3. 这个循环永远不会终止,因为17,000,000 + 1被舍入到17,000,000。 如果你有类似的东西:

    float foo = 10000000 - 10000000.0001
    

    由于舍入误差,foo的值将为0,而不是-0.0001。

答案 2 :(得分:2)

  

我的问题是其他什么   我应该看的常见情况   因为什么被认为是'好'   接近他们的方法?

有几种方法可以造成严重甚至灾难性的精确度损失。

最重要的原因是浮点数的位数有限,例如,双数有53位。这意味着如果你有“无用”的数字不是解决方案的一部分但必须存储,你将失去精确度。

例如(我们使用十进制类型进行演示):

2.598765000000000000000000000100 -

2.598765000000000000000000000099

有趣的部分是100-99 = 1的答案。由于2.598765在两种情况下都相同,所以 不会改变结果,但会浪费8位数。更糟糕的是,因为电脑没有 知道数字是无用的,它被迫存储它并在它之后产生21个零, 浪费在所有29位数。不幸的是,没有办法规避差异, 但还有其他情况,例如exp(x)-1,这是物理学中经常出现的一种函数。

接近0的exp函数几乎是线性的,但它强制使用1作为前导数字。所以12 有效数字 exp(0.001)-1 = 1.00100050017 - 1 = 1.00050017e-3

如果我们使用函数expm1(),请使用taylor系列:

1 + x + x ^ 2/2 + x ^ 3/6 ... -1 =

x + x ^ 2/2 + x ^ 3/6 =:expm1(x)

expm1(0.001)= 1.00500166667e-3

好多了。

第二个问题是具有非常陡峭斜率的函数,如pi / 2附近的x的正切。 tan(11)具有50000的斜率,这意味着由舍入误差引起的任何小的偏差 之前将被因子50000放大!或者你有奇点,例如结果接近0/0,这意味着它可以有任何值。

在这两种情况下,您都可以创建替代函数,简化原始函数。突出不同的解决方案方法是没有用的,因为如果没有培训,你就不会首先“看到”问题。

一本非常好的书来学习和训练:Forman S. Acton:真正的计算成真了

答案 3 :(得分:1)

要避免的另一件事是减去几乎相等的数字,因为这也会导致对舍入误差的敏感度增加。对于接近0的值,cos(x)将接近1,所以1 / cos(x) - 1是您希望尽可能避免的减法之一,所以我想说(a)应该避免