在MATLAB中为什么0.1-0.3+0.2不等于零~~

  • 格式:docx
  • 大小:17.13 KB
  • 文档页数:3

现在MATLAB的Command Window中进行一组运算:
>> 0.1+0.2-0.3
ans =
5.5511e-17
>> 0.1-0.3+0.2
ans =
2.7756e-17
为什么上式的结果不为0呢??且不同的运算顺序结果不一样呢??下面我们就详细解释这个原因!
在本教程之前推荐您先了解下《1985年IEEE发布了二进制浮点运算标准
754-1985》。

根据IEEE浮点数运算标准,我编写了两个简单的程序,用于ieee数值和double 数值之间的转换。

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);
*****************************************
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];
利用上面的double2ieee函数尝试得到0.1的IEEE编码
>>x_double=0.1;
>> x_ieee_01=double2ieee(x_double)
x_ieee_01 =
0011111110111001100110011001100110011001100110011001100110011010
也就是说0.1的IEEE编码就是上面那一坨0和1(晕吧),其实这串二进制代表的真实数据略大于0.1,也就是说
IEEE(0011111110111001100110011001100110011001100110011001100110011001)
<DOUBLE(0.1)<
IEEE(0011111110111001100110011001100110011001100110011001100110011010) 傻子都知道计算机是二进制存储数据的,由于0.1没有精确的IEEE编码,根据就近一致原则,0.1采用的IEEE编码就采用最近的第二个编码。

现在讨论下上面两个编码到底代表什么数据呢?好,使用ieee2double()函数来测试下
>>
x_double_01_left=ieee2double('001111111011100110011001100110011001100110011001 1001100110011001')
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('00111111101110011001100110011001100110011001100 11001100110011010')
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和1对应于上面那两个分数(为了保留足够的精度,这里使用分数显示出来,如果直接采用小数显示,您不会看到区别的)!
同理可以得到0.2和0.3的IEEE编码,以及相应的IEEE编码代表的真实数值!% 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的编码转换
>...。