1701005362
ans = 5.5511e-17 ans = 2.7756e-17
1701005363
1701005364
我们从MATLAB输出结果可看到,上式的结果不为0呢?0.1+0.2-0.3不等于0,0.1-0.3+0.2不等于0,更加神奇的是0.1+0.2-0.3不等于0.1-0.3+0.2,这是为什么?为什么不同的运算顺序结果不一样呢?下面我们详细解释这个原因。
1701005365
1701005366
在此,读者需要先了解《1985年IEEE发布了二进制浮点运算标准754-1985》。
1701005367
1701005368
根据IEEE浮点数运算标准,我编写了两个简单的程序,用于IEEE数值和double数值之间的转换。
1701005369
1701005370
IEEE数值转换为double数值,函数程序文件如下:
1701005371
1701005372
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);
1701005373
1701005374
double数值转换为IEEE数值,函数程序文件如下:
1701005375
1701005376
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];
1701005377
1701005378
利用上面的double2ieee()函数尝试得到0.1的IEEE编码,程序如下:
1701005379
1701005380
clc,clear,close all %清屏和清除变量 warning off %消除警告 x_double=0.1; x_ieee_01=double2ieee(x_double) %将双精度数据转换为IEEE编码
1701005381
1701005382
运行程序输出结果如下:
1701005383
1701005384
x_ieee_01 = 0011111110111001100110011001100110011001100110011001100110011010
1701005385
1701005386
也就是说0.1的IEEE编码就是由一系列0和1组成,其实这串二进制代表的真实数据略大于0.1,也就是说:
1701005387
1701005388
ieee(0011111110111001100110011001100110011001100110011001100110011001) < double(0.1) < ieee(0011111110111001100110011001100110011001100110011001100110011010)
1701005389
1701005390
我们都知道计算机是二进制存储数据的,由于0.1没有精确的IEEE编码,根据就近一致原则,0.1采用的IEEE编码就采用最近的第二个编码。
1701005391
1701005392
【问题】0.1两个编码到底代表什么数据呢?
1701005393
1701005394
【分析】
1701005395
1701005396
下面使用ieee2double()函数来测试如下:
1701005397
1701005398
clc,clear,close all %清屏和清除变量 warning off %消除警告 x_double_01_left=ieee2double(‘0011111110111001100110011001100110011001100110011001100110011001’) %将IEEE编码转换为双精度数据 ans1 = double(x_double_01_left)-0.1 %可以看出,第一个IEEE编码和0.1还是有差距的 x_double_01_right=ieee2double(‘0011111110111001100110011001100110011001100110011001100110011010’) ans2 = double(x_double_01_right)-0.1 %第二个IEEE编码和0.1就没有区别了
1701005399
1701005400
运行程序输出结果如下:
1701005401
1701005402
x_double_01_left = 7205759403792793/72057594037927936 ans1 = -1.3878e-17 x_double_01_right = 3602879701896397/36028797018963968 ans2 = 0
1701005403
1701005404
由结果可以看出,第一个IEEE编码和0.1还是有差距的;第二个IEEE编码和0.1没有区别,然而它也不是0.1的真实编码,而是距离最近的一个,换句话说0.1是没有准确的IEEE编码的,当然还有很多数据也没有准确的IEEE编码。
1701005405
1701005406
同理可以得到0.2和0.3的IEEE编码,以及相应的IEEE编码代表的真实数值,程序如下:
1701005407
1701005408
%0.2的编码转换 clc,clear,close all %清屏和清除变量 warning off %消除警告 x_ieee_02=double2ieee(0.2) %0.2 IEEE编码 x_double_02=ieee2double(x_ieee_02) %0.3的编码转换 x_ieee_03=double2ieee(0.3) %0.3 IEEE编码 x_double_03=ieee2double(x_ieee_03)
1701005409
1701005410
运行程序输出结果如下:
1701005411
[
上一页 ]
[ :1.701005362e+09 ]
[
下一页 ]