关于浮点数的误差理解
拜讀了Cleve Moler寫的博客《Bank Format and Metric Socket Wrenches》,對浮點數又有了進一步的理解。原文的鏈接地址為
http://blogs.mathworks.com/cleve/2017/04/10/bank-format-and-metric-socket-wrenches/
本文的內容是對這篇文章部分翻譯以及自己的一些理解,讓大家能夠更加容易看懂。
從format bank這個問題而來
我們知道matlab中有一些命令可以控制顯示格式,比如:
format short: 默認,顯示較少的有效數字
format long:顯示較多的有效數字
format hex:用16進制顯示
format short g:用8進制顯示
這些都比較常用,在金融銀行業中用的比較多的就是format bank,這個命令保留小數點后2位(四舍五入,保留2位小數)
注意上面的命令只是用來控制顯示的格式,而不影響計算的精度
在使用format bank時可能會遇到讓你覺得奇怪的現象,如果你對計算機內的浮點數表示不了解的話。以下是一個奇怪的例子
format long x = (5:10:45)' / 1000 y = 1 + x z = 2 + xx =
0.005000000000000
0.015000000000000
0.025000000000000
0.035000000000000
0.045000000000000
y =
1.005000000000000
1.015000000000000
1.025000000000000
1.035000000000000
1.045000000000000
z =
2.005000000000000
2.015000000000000
2.025000000000000
2.035000000000000
2.045000000000000
x =
0.01
0.01
0.03
0.04
0.04
y =
1.00
1.01
1.02
1.03
1.04
z =
2.00
2.02
2.02
2.04
2.04
是不是覺很奇怪,變量x, y, z中有些數字四舍五入了,有些沒有四舍五入。其實這就是10進制小數不能完全用2進制浮點數精確表示引起的。
雖然在matlab中顯示的數字等于 0.015000000000,好像很精確了,但其實在matlab內部這個數卻還沒有達到0.015,比0.015略小一點。 下面可以證實這一點
exact_x = sym(x, 'e') exact_y = sym(y, 'e') exact_z = sym(z, 'e')sym(x, ‘e’) 返回的就是在matlab中表示的數值x,結果的類型是一個符號數。
exact_x =
eps/2133 + 1/200
3/200 - eps/400
eps/160 + 1/40
(3*eps)/200 + 7/200
9/200 - (3*eps)/400
exact_y =
201/200 - (12*eps)/25
203/200 - (11*eps)/25
41/40 - (2*eps)/5
207/200 - (9*eps)/25
209/200 - (8*eps)/25
exact_z =
401/200 - (12*eps)/25
(14*eps)/25 + 403/200
81/40 - (2*eps)/5
(16*eps)/25 + 407/200
409/200 - (8*eps)/25
可以看到所有的數都比理論值多一點或者少一點,其實這個誤差是怎么來的,為什么會有這個誤差,其實就跟浮點數的表示有關。我很早寫過一篇關于浮點數如何表示的文章,大家可以參看一下,文章地址http://blog.csdn.net/whoispo/article/details/6312782。在下一節中,我會計算這個誤差,大家最好能夠理解浮點數的表示。
因此對于上面的變量x, y, z,有些進行了四舍五入,有些沒有的原因,就比較好理解了。數字比0.5稍小的,就舍去了,比0.5稍大的就入位。
format short signx = sign(double(exact_x - x)) signy = sign(double(exact_y - y)) signz = sign(double(exact_z - z))signx =
1
-1
1
1
-1
signy =
-1
-1
-1
-1
-1
signz =
-1
1
-1
1
-1
等于1就表示matlab中表示值比理論值大,因此入位,等于-1就表示matlab的表示值比理論值小,就舍去。大家看看是不是和format bank顯示的結果一致。
計算浮點數的誤差
下面就來計算一下浮點數 0.1 的誤差。
我們知道0.1在1/16和1/8之間,因此可以確定0.1的浮點數的指數部分應該就是1/16,剩下的就是尾數部分。一般而言,如果不對matlab的類型做特殊說明,matlab的類型大多都是double類型。double類型的尾數部分有52位,也就是2?52,再加上指數部分的2?4,因此對于1/16到1/8之間的數,相鄰兩個數之間的間隔為2?56,也即0.1附近的數的間隔為2?56。用matlab驗證一下
eps(0.1) 2^(-56)ans =
1.3878e-17
ans =
1.3878e-17
y = eps(x) 定義就是計算浮點數集中比x?大一點的數與x?的差值,也就是計算x附近的浮點數的間隔。注意我在這里的x右上角加了一個”*”,表示計算機中x的值。
我們知道了間隔,就很容易得到相鄰的比x小一點的數,我們記為x?,和相零的比x大一點的數了,我們記為x+。x?取哪一個數,取決于x離x?近還是離x+近。
x?和x+的計算
因為
floor(?1/16gap)?gap+1/16=0
所以
x?=floor(0.1gap)?gap
同理
x+=ceil(0.1gap)?gap x = 2^(-56) symx = sym(x) x0 = floor(0.1/symx)*symx x1 = ceil(0.1/symx) * symx
x =
1.3878e-17
symx =
1/72057594037927936
x0 =
7205759403792793/72057594037927936
x1 =
3602879701896397/36028797018963968
為了計算x離x?近還是離x+近,下面只能采用matlab的符號計算,為了避免丟失精度,因為x0和x1已經是符號變量了,所以可以直接構造符號變量c。
c = [x0 0.1 x1]'c =
7205759403792793/72057594037927936
1/10
3602879701896397/36028797018963968
這里的c(2)精確等于0.1,因為這是一個符號。用vpa函數計算精確值,這是對符號數進行高精度計算的方法,可以任意指定精度
vpa(c, 50)ans =
0.099999999999999991673327315311325946822762489318848
0.1
0.1000000000000000055511151231257827021181583404541
從這里可以看到,用雙精度浮點數確實不能精確表示0.1
double(c(1)) double(c(3))ans =
0.1000
ans =
0.1000
ans =
0.6
ans =
0.4
因此可以看到x還是離x+近一點。所以0.1在計算機中的存儲的值為
在計算機中
eps(0.1)=2?56
因此
用matlab驗證一下
x = 0.1 e = sym(x, 'e')x =
0.1000
e =
eps/40 + 1/10
與預想結果一致。
總結
以上是生活随笔為你收集整理的关于浮点数的误差理解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python中的enumerate的用法
- 下一篇: MATLAB中plot函数的linesp