|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
8 M7 O: F2 a, `. b+ {5 M1 q现在MATLAB的Command Window中进行一组运算
- `. @" ~9 E$ v' X+ w- >> 0.1+0.2-0.3
- ans =
- 5.5511e-17
- >> 0.1-0.3+0.2
- ans =
- 2.7756e-17# z8 n5 D. f4 L; V2 Y) B
8 Z! s+ I6 |. f6 f' D
- s3 |3 c3 e9 w* n( [/ P0 L, d% B
为什么上式的结果不为0呢??且不同的运算顺序结果不一样呢??下面我们就详细解释这个原因!1 _; ?1 |2 v* H4 u4 e+ D( }- a6 L$ H
" K- r$ } V* U S/ d) G# _8 ?! ]在本教程之前推荐您先了解下《1985年IEEE发布了二进制浮点运算标准754-1985》。根据IEEE浮点数运算标准,我编写了两个简单的程序,用于ieee数值和double数值之间的转换。
1 b; P+ f( f0 q& Q- X" d& f- function [x_double,s,c,f]=ieee2double(x_ieee)
- % 将IEEE编码转换为双精度数据
- % x_double=(-1)^s*2^(c-1023)*(1+f),双精度数据
- % x_ieee,IEEE编码
- % s,符号位,长度1
- % c,指数位,长度11
- % f,尾数位,长度52
- %
- s=bin2dec(x_ieee(1));
- c=bin2dec(x_ieee(2:12));
- m=bin2dec(x_ieee(13:64)');
- % 为了保证精度,使用符号运算
- f=sym('1/2').^(1:52)*m;
- x_double=(-1)^s*2^(c-1023)*(1+f);% {. j6 ~* s9 A& Y- g
& V" V- I: W# U! q3 H+ T M& D
' H+ J; U, b- s1 H* z8 o- function [x_ieee,s,c,f]=double2ieee(x_double)
- % 将双精度数据转换为IEEE编码
- % x_double=(-1)^s*2^(c-1023)*(1+f),双精度数据
- % x_ieee,IEEE编码
- % s,符号位,长度1
- % c,指数位,长度11
- % f,尾数位,长度52
- if x_double>0
- s='0';
- else
- s='1';
- end
- n=floor(log2(x_double));
- c=dec2bin(n+1023,11);
- f=dec2bin(round((x_double/2^n-1)*2^52),52);
- x_ieee=[s,c,f];' P0 V$ @8 `! \4 t
1 W! S3 i3 y! z5 K' `
. j4 E# `9 q( t7 c. f
利用上面的double2ieee函数尝试得到0.1的IEEE编码
7 ^8 j8 B& A" Z. h3 b1 N! \, y6 B- >> x_double=0.1;
- >> x_ieee_01=double2ieee(x_double)
- x_ieee_01 =
- 0011111110111001100110011001100110011001100110011001100110011010
1 U2 M5 J! U1 ~( k" Q8 I
$ E' G# J0 i: B% H' [' [ s* t# g$ K- O" Z# y/ P5 s: E! d
也就是说0.1的IEEE编码就是上面那一坨0和1(晕吧),其实这串二进制代表的真实数据略大于0.1,也就是说
( \3 f6 H+ I" E+ v- IEEE(0011111110111001100110011001100110011001100110011001100110011001)
- <<br style="word-wrap: break-word; ">
- IEEE(0011111110111001100110011001100110011001100110011001100110011010), U$ X+ m! H) k# a. v
2 y* r) x6 Z3 i) A/ V
' b' `9 O8 f9 e6 H# t
傻子都知道计算机是二进制存储数据的,由于0.1没有精确的IEEE编码,根据就近一致原则,0.1采用的IEEE编码就采用最近的第二个编码。
9 w0 t- |1 ^. ^: c3 \6 g; T# ?/ o7 {+ G( h/ _* P
现在讨论下上面两个编码到底代表什么数据呢?好,使用ieee2double()函数来测试下
- ^& Q5 M; v; w* ~( P+ S- >> x_double_01_left=ieee2double('0011111110111001100110011001100110011001100110011001100110011001')
- x_double_01_left =
- 7205759403792793/72057594037927936
- >> double(x_double_01_left)-0.1 % 看到没有,第一个IEEE编码和0.1还是有差距的
- ans =
- -1.3878e-17
- >> x_double_01_right=ieee2double('0011111110111001100110011001100110011001100110011001100110011010')
- x_double_01_right =
- 3602879701896397/36028797018963968
- >> double(x_double_01_right)-0.1 % 第二个IEEE编码和0.1就没有区别了,但是第二个IEEE编码也不是0.1的真实编码,而是距离最近的一个,换句话说0.1是没有准确的IEEE编码的,当然还有很多数据也没有准确的IEEE编码
- ans =
- 0) ?8 R0 s2 Y" W5 X: V( r' [) }
$ |5 y" f) T. r4 J' I$ D) V- h
1 U8 N V& {5 L0 M也就是说那一大串0和1对应于上面那两个分数(为了保留足够的精度,这里使用分数显示出来,如果直接采用小数显示,您不会看到区别的)!
7 {: Z# V% X) _( i2 B& N& S$ {
! I9 L* z: O, v同理可以得到0.2和0.3的IEEE编码,以及相应的IEEE编码代表的真实数值!
+ s, Y, ~0 N, U0 k5 V- o- % 0.1的编码转换
- >> x_ieee_01=double2ieee(0.1) % 0.1 IEEE编码
- x_ieee_01 =
- 0011111110111001100110011001100110011001100110011001100110011010
- >> x_double_01=ieee2double(x_ieee_01)
- x_double_01 =
- 3602879701896397/36028797018963968
- % 0.2的编码转换
- >> x_ieee_02=double2ieee(0.2) % 0.2 IEEE编码
- x_ieee_02 =
- 0011111111001001100110011001100110011001100110011001100110011010
- >> x_double_02=ieee2double(x_ieee_02)
- x_double_02 =
- 3602879701896397/18014398509481984
- % 0.3的编码转换
- >> x_ieee_03=double2ieee(0.3) % 0.3 IEEE编码
- x_ieee_03 =
- 0011111111010011001100110011001100110011001100110011001100110011
- >> x_double_03=ieee2double(x_ieee_03)
- x_double_03 =
- 5404319552844595/18014398509481984
% ^3 P+ O' y! ^6 `& M( I
, O: B7 v1 I% {* j6 o
; D, w+ h4 q& ]现在模拟计算0.1+0.3-0.2的结果
# v" ~0 M7 G0 H* i: J- e- >> x_double_01-x_double_03+x_double_02
- ans =
- 1/36028797018963968
- >> 1/36028797018963968
- ans =
- 2.7756e-17
- >> 0.1-0.3+0.2
- ans =
- 2.7756e-17# l- O# r; q) f0 `1 q
' S$ r( G0 d9 n1 t+ a1 w
+ H2 L' R u, }. j2 Z( W
也就是说在IEEE标准下,0.1+0.3-0.2的结果为1/36028797018963968≈2.7756e-17,显然这个不等于零!!# w9 n/ E W+ U! H5 `! h
' }4 k% Z& L% A4 F. v9 B
接下来讨论下,为什么0.1-0.3+0.2和0.1+0.2-0.3的结果不一样?
& H; ~5 D/ x9 O3 S/ W- ~1 }9 B& k
这个主要是由于加法运算是左结合的,也就是说0.1-0.3+0.2是先计算0.1-0.3,得到-0.2;而0.1+0.2-0.3是先计算0.1+0.2,得到0.3。-0.2和0.3的IEEE编码当然是不同的,相应的误差也有区别,于是得到最后结果也就不同了。至于具体多少,大家可以使用本文提供的两个函数进行测试和推到下! |
|