2019年2月17日 星期日

【数值分析】0.4 有效数字缺失

【数值分析】0.4 有效数字缺失

Abstract: 本文介绍计算机在计算中有可能引发的有效数字缺失
Keywords: 有效数字缺失

有效数字缺失

对计算机硬件或者底层功能足够了解,虽然表面教条枯燥,但是我们能在其中学到很多有可能出现,但是不容易察觉的陷阱

有效数字

在了解有效数字缺失之前,我们有必要确定下什么是有效数字,无论是小数还是大数,我们都能写成科学计数法的形式,在十进制下我们可以把所有数字写成:
d1.d2dn×10p

的这种形式,其中 d10,有效数字是 d1.d2dn 共 n 位,注意我们这里之研究数学上的有效数字,物理中测量还有另一套说法,我们不考虑.
有效数字我们知道是什么了,那么什么情况下会缺失呢?数字不会凭空消失,一定是做了某种计算后,原本有 n 位有效数字,结果变成了小于 n 位,这就是缺失,而缺失不可能发生在右侧,也就是 dn 这一侧,原因是,如果这些位消失了,我们依然可以补充0作为有效数字,所以我们要注意左侧的数字。
比如:
123.4567123.4566=000.0001

这时候,我们原本的7位有效数字,瞬间变成1位,有问题么?没有问题,因为这是准确结果,但是从数值上看,有效数字减少了。
但是,如果减法的两个操作数,减数或者被减数,并不是一个确定的数,而是一个表达式,包含无限的小数呢?

“减法”=“陷阱”

考虑 9.013 这个表达式的值。
这个例子很简答,输入matlab能够直接得到答案,9.013.001662 所以 9.013|0.001662 减法之前有七位有效数字,结果有4位有效数字,也就是少了3位,这看起来没啥。
然后我们假定,我们有一种特殊的计算机,其浮点数最多只能保存十进制的3位有效数字,(书中说是三位计算机,表达不是很准确,会和三个二进制位混淆,所以我们说这个计算机只能计算3位十进制)那么我们如果按照上面的计算过程,那么会得到:
9.013.001662=3.009.013=0

结果是0,第一步我们把3.001662保存成3.00 的原因是计算机只能保存3位十进制数,所以这个结果是截断误差造成的,然后相减得到了|0.00,理想情况,有效数字是1.66,可见一个都不对。
我们这里所说的有效数字是和真实计算结果数字上的比较,不是比较大小,只考虑尾数,不考虑指数。
两个相近的数相减,可能会造成有效数字的缺失
但是你可能说,没办法,这是机器的限制,你那个能存一百位有效数字的计算机来就可以了,如果你这么想,那就没得聊了,
有没有办法?当然有办法!

解决方法

解决办法的核心思想是:把相近的两个数变成不相近的!
同样使用只有三维有效数字的计算机
9.013=(9.013)(9.01+3)9.01+3=|0.013.00+3=16=1.67×103

上面这种方法叫做共轭等式,可以避免两个相近的数字相减。下面我们看另一个例子,主要技巧也是用一些恒等式:
E1=1cosxsin2x

明显得当x0 的时候 cosx1 那么就有可能造成有效数字缺失,与上面类似的,我们也有共轭等式:
E2=(1cosx)(1+cosx)sin2x(1+cosx)=1(1+cosx)=E1

然后我们就写个代码来观察下 E1 和 E2 在 x0 的时候的表现,是否 E2 更准确,我们写一段python程序:

1
2
3
4
5
6
7
8
9
import math
x=1.0
for i in range(0,16):
    x=x/10
    cos_x=math.cos(x)
    sin_x=math.sin(x)
    e1=(1- cos_x)/pow(sin_x,2)
    e2 = (1 ) / (1 + cos_x)
    print '|%1.16f|%1.16f|%1.16f|' % (x,e1,e2)
运行结果是:
xE1E2
0.10000000000000000.50125208628857690.5012520862885712
0.01000000000000000.50001250020848050.5000125002083363
0.00100000000000000.50000012499218940.5000001250000208
0.00010000000000000.49999999862793110.5000000012500000
0.00001000000000000.50000004138685220.5000000000125000
0.00000100000000000.50004445029133700.5000000000001250
0.00000010000000000.49960036108132190.5000000000000012
0.00000001000000000.00000000000000000.5000000000000000
0.00000000100000000.00000000000000000.5000000000000000
0.00000000010000000.00000000000000000.5000000000000000
0.00000000001000000.00000000000000000.5000000000000000
0.00000000000100000.00000000000000000.5000000000000000
0.00000000000010000.00000000000000000.5000000000000000
0.00000000000001000.00000000000000000.5000000000000000
0.00000000000000100.00000000000000000.5000000000000000
0.00000000000000010.00000000000000000.5000000000000000
从 0.00000001开始 E1 崩溃,而 E2 保持稳定。
到0.00000001崩溃的原因是cos在这个位置截断后就是1.0:
re-2
同样,我们这套方法的核心就是不要见相近的数字,只要有减法就要小心,注意前后两个数是否相近,另一个例子是二次方程的求解,对于公式法我们必须要计算:
x=b±b24ac2a

其中要注意的是
x1=b+b24ac2a

对应的方法就是把这个计算改成:
x1=(b+b24ac)(+b+b24ac)2a(+b+b24ac)=4ac2a(+b+b24ac)=2c(b+b24ac)

当然这里面还有问题就是 b24ac 的部分依然存在减法
而且 b 是负数的时候,依然会存在 b+b24ac 相近的情况,尤其是 ac<<b2 的时候,这时候计算最好使用:
x1=b+b24ac2ax2=2cb+b24ac

总结

有效数字可能会带来很小的误差,也可以带来很大的误差,所以,不管什么时候,我们能避免减法就避免减法,尤其减法的两个数字接近的时候,这个时候更危险。

沒有留言:

張貼留言

2024_09 作業3 以Node-Red 為主

 2024_09 作業3  (以Node-Red 為主  Arduino 可能需要配合修改 ) Arduino 可能需要修改的部分 1)mqtt broker  2) 主題Topic (發行 接收) 3) WIFI ssid , password const char br...