Floating point 两个双精度数商的精度
假设我想数值计算f(x)=cos(x)在x=pi/2时的一阶导数。df/dx=-sin(x),因此是df/dx=-1。为此,我使用最简单的公式: df/dx=(f(x+h)-f(x))/h+O(h) 这里O(h)是误差,它与h成正比,因此,数学上O(h)随着h变为0而变为0。我知道在电脑里情况是不同的 如果我使用的是双精度,即15到17位十进制有效数字,我应该能够接近精确结果df/dx=-1.0乘以~10^-15吗?也就是说,我应该找到(df/dx)_数值+1.0~10^-15吗 这是我发现的不同h值: h值:Floating point 两个双精度数商的精度,floating-point,double,precision,Floating Point,Double,Precision,假设我想数值计算f(x)=cos(x)在x=pi/2时的一阶导数。df/dx=-sin(x),因此是df/dx=-1。为此,我使用最简单的公式: df/dx=(f(x+h)-f(x))/h+O(h) 这里O(h)是误差,它与h成正比,因此,数学上O(h)随着h变为0而变为0。我知道在电脑里情况是不同的 如果我使用的是双精度,即15到17位十进制有效数字,我应该能够接近精确结果df/dx=-1.0乘以~10^-15吗?也就是说,我应该找到(df/dx)_数值+1.0~10^-15吗 这是我发现的不
[0.0001, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09, 1e-10]
(df/dx)\数值:
[-0.9999999983332231,
-0.9999999999898844,
-0.9999999999175667,
-1.0000000005838656,
-0.999999993922529,
-1.000000082740371,
-1.000000082740371]
这是预期的吗?为什么?为什么h=10^-5得到的结果最好?使用有限差分法计算导数容易出错。问题是你在做减法的环境,而不是你在做除法。基本上,
cos(x)-cos(x+delta)
在cos(x)
和cos(x+delta)
都接近于零时,可以正常工作。。。但当它们远离零(但仍然彼此接近)时,结果的精度将急剧下降。在某一点上,由于使用较小的增量而增加的精度将被显著性损失导致的精度降低所抵消。对你来说,这看起来像是发生在1e-5左右,但这并不是根本性的(在那个地区,错误往往会反弹很多)
关于如何进行数值稳定的有限差分,已经写了很多文章,但第一条规则是“不要对微小的差分过于贪婪”。要获得更深入(且不那么模糊)的信息,我可以推荐(通常)有效的Forman Acton的数值方法
编辑:
对不起,我忘了提最重要的一点。分子中的显著性损失很重要,但x+h
中的显著性损失更为重要。这部分其实很容易修复
当x
变大时,x+h
的近似值变差(同样,失去显著性)。基本上,用于计算的步长与分母中的步长不匹配。但是由于您并不真正关心h
的确切值,因此您可以计算出在x+h
四舍五入后最终使用的h
值,并在分母中使用该值。基本上,您可以计算x2=x+h
,然后h'=x2-x
。当x>>h
(斯特本茨定理)时,h'
的计算是精确的,消除了这种特殊的意义损失
示例代码:
import math
def calcDerivAt_orig(f, x, h):
x1 = x
x2 = x+h
y1 = f(x1)
y2 = f(x2)
return (y2-y1)/h
def calcDerivAt_fixed(f, x, h):
x1 = x
x2 = x+h
y1 = f(x1)
y2 = f(x2)
return (y2-y1)/(x2-x1)
for h in [0.0001, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09, 1e-10]:
dOrig = calcDerivAt_orig(math.cos, math.pi/2, h)
origErr = abs(-1 - dOrig)
dFixed = calcDerivAt_fixed(math.cos, math.pi/2, h)
fixedErr = abs(-1 - dFixed)
print("h = {}: origErr = {}, fixedErr = {}".format(h, origErr, fixedErr))
产生:
h = 0.0001: origErr = 1.66677693869e-09, fixedErr = 1.66666680457e-09
h = 1e-05: origErr = 1.01155750443e-11, fixedErr = 1.6666779068e-11
h = 1e-06: origErr = 8.24332824223e-11, fixedErr = 1.66644475996e-13
h = 1e-07: origErr = 5.83865622517e-10, fixedErr = 1.55431223448e-15
h = 1e-08: origErr = 6.07747097092e-09, fixedErr = 0.0
h = 1e-09: origErr = 8.27403709991e-08, fixedErr = 0.0
h = 1e-10: origErr = 8.27403709991e-08, fixedErr = 0.0
一点也不坏。当然,我们选择x=pi/2
是在作弊,因为cos(x)
在这附近接近于零;在x=1.5
或类似情况下,当h
从我最初描述的显著性损失缩小时,您仍然会看到错误中出现气球:
h = 0.0001: origErr = 3.53519750529e-06, fixedErr = 3.5351976152e-06
h = 1e-05: origErr = 3.53675653653e-07, fixedErr = 3.53669118991e-07
h = 1e-06: origErr = 3.52831192041e-08, fixedErr = 3.53651797846e-08
h = 1e-07: origErr = 4.03034106089e-09, fixedErr = 3.44793649187e-09
h = 1e-08: origErr = 5.5453325265e-09, fixedErr = 5.1691428915e-10
h = 1e-09: origErr = 7.21702790862e-08, fixedErr = 1.03628251535e-08
h = 1e-10: origErr = 1.6659127966e-08, fixedErr = 6.58739718329e-08