|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
0 |/ P9 ^( `! u% I: [, Q2 X4 r( \
现在MATLAB的Command Window中进行一组运算7 D! j8 \: E) V5 d+ k8 V
- >> 0.1+0.2-0.3
- ans =
- 5.5511e-17
- >> 0.1-0.3+0.2
- ans =
- 2.7756e-17# V% Y- D8 C. t9 P/ `5 T
0 V+ \7 P7 T8 c6 F, {1 g8 g
2 h0 }6 o0 r3 j5 m* m为什么上式的结果不为0呢??且不同的运算顺序结果不一样呢??下面我们就详细解释这个原因!
, J( }) w4 e: G6 ?7 d- l( N5 k3 R9 E0 }0 R2 O: I6 i' y
在本教程之前推荐您先了解下《1985年IEEE发布了二进制浮点运算标准754-1985》。根据IEEE浮点数运算标准,我编写了两个简单的程序,用于ieee数值和double数值之间的转换。
# d/ Z3 [4 l/ b/ h+ v- 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);- A0 b- x- e& s) j8 A
) D2 b6 W. p4 Q6 M/ T
/ d& V+ d2 W7 P$ Y! }- 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];6 ]" B/ F2 ~, w& X, I+ x
5 g! c f9 u2 Y) \
) k2 B$ [9 ^8 Q3 z利用上面的double2ieee函数尝试得到0.1的IEEE编码
0 F6 X r4 X5 ?0 H- >> x_double=0.1;
- >> x_ieee_01=double2ieee(x_double)
- x_ieee_01 =
- 00111111101110011001100110011001100110011001100110011001100110108 a: }: G3 r6 z; n
+ v$ @. l# b0 ^! Q5 l, [' q* ^( x
4 x9 ]8 x$ C4 d: y$ i也就是说0.1的IEEE编码就是上面那一坨0和1(晕吧),其实这串二进制代表的真实数据略大于0.1,也就是说
' J9 Q& g1 j2 J8 K# s5 o& H- IEEE(0011111110111001100110011001100110011001100110011001100110011001)
- <<br style="word-wrap: break-word; ">
- IEEE(0011111110111001100110011001100110011001100110011001100110011010)
" r1 ^$ x) `# B ?7 U4 I$ F ' ~! t9 i% f+ f6 n( O* Q" B
+ n7 j C2 B) X t2 O# u
傻子都知道计算机是二进制存储数据的,由于0.1没有精确的IEEE编码,根据就近一致原则,0.1采用的IEEE编码就采用最近的第二个编码。
9 U% a j( U) T% W* n, W6 d, E( ^7 z+ f: M
现在讨论下上面两个编码到底代表什么数据呢?好,使用ieee2double()函数来测试下
8 ^; V* V( M; z. y' m- >> 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
3 z$ {' c; G4 S+ w0 j4 d
& _- t7 Z$ S% u# d$ ]$ F3 k1 u
9 _- E* f( s9 v$ e2 ]4 B c也就是说那一大串0和1对应于上面那两个分数(为了保留足够的精度,这里使用分数显示出来,如果直接采用小数显示,您不会看到区别的)!$ t) ?1 D4 t9 E
( O" z7 ~& G ?+ m8 W! l, U
同理可以得到0.2和0.3的IEEE编码,以及相应的IEEE编码代表的真实数值!8 q0 b4 ?" }' h
- % 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
$ }' R; s$ r& L' U, X A' B, H - ^0 P6 R8 t6 ~) b4 u
2 a7 o% t; G' J5 U& k2 J! ?现在模拟计算0.1+0.3-0.2的结果2 v2 r2 @: z. o! U
- >> 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
% b# Q( J4 p y! {. Z' U9 W) C- l# y # p; l F: ~7 @
& w0 T8 C$ U7 c& {9 l- }* a% r# f
也就是说在IEEE标准下,0.1+0.3-0.2的结果为1/36028797018963968≈2.7756e-17,显然这个不等于零!!
& [1 t* ?% k# {5 i7 I& P5 ]( S+ Q" C5 p" ~4 H: F$ ?: {- O% ~4 x
接下来讨论下,为什么0.1-0.3+0.2和0.1+0.2-0.3的结果不一样?
1 ]) l( ]+ B* o. d: B" l" y
9 [! m) y( X3 J4 ~0 u5 Y4 q1 L这个主要是由于加法运算是左结合的,也就是说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编码当然是不同的,相应的误差也有区别,于是得到最后结果也就不同了。至于具体多少,大家可以使用本文提供的两个函数进行测试和推到下! |
|