现在的位置: 首页 > 综合 > 正文

浅谈计算机中浮点数的表达方法(IEEE 754)

2017年10月17日 ⁄ 综合 ⁄ 共 4671字 ⁄ 字号 评论关闭

浅谈计算机中浮点数的表达方法(IEEE 754)

   
  
计算机中是如何存储和表达数字的?对于整数,情况比较简单,直接按照数学中的进制转换方法处理即可,即连续除以2取余。这并不是难点,真正的难点在于小数是如何转换为二进制码(即浮点数)的。

当然,从数学的角度来讲,十进制的小数可以转换为二进制小数(整数部分连续除2,小数部分连续乘2),例如125.125D=1111101.001B,但问题在于计算机根本就不认识小数点“.,更不可能认识1111101.001B。那么计算机是如何处理小数的呢?

历史上计算机科学家们曾提出过多种解决方案,最终获得广泛应用的是IEEE
754标准中的方案,目前最新版的标准是IEEE std 754-2008
。该标准提出数字系统中的浮点数是对数学中的实数(小数)的近似,同时该标准规定表达浮点数的01序列被分为三部分(三个域):

32位单精度浮点数为例,其具体的转换规则是:首先把二进制小数(补码)用二进制科学计数法表示,比如上面给出的例子1111101.001=1.111101001*2^6。符号位sign表示数的正负(0为正,1为负),故此处填0exponent表示科学计数法的指数部分,请务必注意的是,这里所填的指数并不是前面算出来的实际指数,而是等于实际指数加上一个数(指数偏移),偏移量为2^(e-1)-1,其中eexponent的宽度(位数)。对于32位单精度浮点数,exponent宽度为8,因此偏移量为127,所以exponent的值为133,即10000101。之后的fraction表示尾数,即科学计数法中的小数部分11110100100000000000000(共23位)。因此32位浮点数125.125D在计算机中就被表示为01000010111110100100000000000000

     对于32位单精度浮点数,sign1位,exponent8位(指数偏移量是127fraction23位。对于64位双精度浮点数,sign1为,exponent11位(指数偏移量是1023fraction52位。

     需要指出的是125.125D的转换结果实际上是规约形式的浮点数,即exponent的数值大于0且小于2^e-1,默认科学计数法中整数部分为1,因此尾数只保留了小数部分。但当数值非常接近于0时,可能出现exponent的数值等于0,且科学计数法中整数部分为0的情况,这就称为非规约形式的浮点数。对此IEEE
std 754-2008规定:非规约形式浮点数的exponent值等于同种情况下规约形式浮点数的exponent再加1。比如exponent=1,显然这是规约形式浮点数,其实际指数应该是-126;而exponent=0,这是非规约形式浮点数,(若按照规约形式浮点数计算,其实际指数应为-127)那么根据前面提到的标准可知这个非规约形式浮点数的实际指数也是-126所有的非规约浮点数比规约浮点数更接近0对于32位单精度浮点数而言,最大的非规约数是(1-2^-23)*2^-1261.18*10^-38,最小的非规约数是2^-23*2^-126=2^-1491.40*10^-45。对于 64 为双精度浮点数而言,最大的非规约数是(1-2^-52)*2^-10222.22*10^-308,最大的非规约数是2^-52*2^-10224.94*10^-324

 
   由上面的内容可以知道,浮点数能表示的范围其实是有限的,它只能表示整条数轴中的三部分:某个很大的负数到某个很接近于
0的负数、0、某个很接近于0的整数到某个很大的正数。此外,由数学分析的知识可知实数是“稠密”的,可以证明在任意两个不相等的实数之间总有无穷多个两两不等的实数;但浮点数不是这样,浮点数是“稀疏”的,两个浮点数之间只有有限个浮点数,并且两个“相邻”的浮点数之间的距离可能是巨大的,这就会带来精度方面的一系列问题。
     
譬如两个“相邻”的32位单精度浮点数,它们的符号位和指数位都相同,尾数位的前22位都相同,只有最后一位相差1,那么这两个浮点数之间的差值可能是非常惊人的。例如0111111010000000000000000000000101111110100000000000000000000000,在32位单精度情况下,它们是“相邻”的,但它们之间的差值竟高达1.014*10^31换句话说,在32位单精度浮点数中,处于这段差值以内的数都无法表示。如果以相对误差来讨论的话,32位单精度浮点数的尾数只有23位,第24位及其后的值会被舍入,可以近似认为其相对误差为2^-231.20*10^-7。这对于某些需要上亿甚至百亿次迭代的程序而言是无法接受的。而64位双精度浮点数的相对误差可以近似认为是2^-522.22*10^-16,比32位单精度浮点数的精度高出不少。可见,64位双精度浮点数不仅表示数的范围扩大了,而且它所刻画的浮点数分布更加“细密”,相对误差更小。并且,对于64位线宽度的计算机而言,处理64位双精度浮点数与处理32位双精度浮点数所需的开销相同,并不需要额外的循环移位,因此还是建议使用64位双精度浮点数。

    当然,浮点数位数越多,其相对误差也就越小,只要它的精度满足程序运行需要就可放心使用。但无论如何,浮点数终究只是实数的粗糙近似,浮点数不可能完全刻画实数,因为浮点数的位数终究是有限的,换句话说它所能表示的总是有限个有理数,而根据数学分析的知识,在实数轴中虽然无理数和有理数都是无限多的,但无理数集是不可数的,而有理数集却是可数的。

        除了上面的内容以外,在编程中需要特别注意的有两点:

一、浮点数都是带符号的,不存在unsigned
doubleunsigned float

二、两个浮点数之间不能用==来判断是否相等,因为浮点数是对实数的近似,所以计算机中两个浮点数不可能完全相等,最多也只能保证其差值小于用户规定的误差限度。

        


 
  最后是一段程序,可以用来将用户输入的小数转换为
IEEE
754标准规定的32
位单精度浮点数


#include "StdAfx.h"            // 这一句是VS2010自动添加的

#include <iostream>

#include <bitset>

 

using namespace std;

 

void main()

{

         float input;

         _ULonglong nMem;

         cout<<"本程序可以将用户输入的小数按照IEEE-754标准转换为二进制码。"<<endl;

         cout<<endl;

         while(1)

         {

                   cout<<"请输入要转换的小数:"<<endl;

                   cin>>input;

                   nMem = *(_ULonglong *)&input;             // 获取内存中保存的input的值

                   bitset<32> mybit(nMem);                         
// 按照32位浮点数格式表达

                   cout<<"转换结果为:"<<endl;

                   cout<<mybit<<endl;

                   cout<<endl;

         }

}


其运行截图为:

将其中的float换成doublebitset<32>换成bitset<64>即可转换为64位双精度浮点数:

上图中指数部分exponent10000000101,即1029=6+1023)。尾数部分fraction不变,只是在后面补0而已。


反过来,如果要将二进制码转换为小数,只需要稍微修改程序即可:

#include "stdafx.h"

#include <bitset>

#include <iostream>


using namespace std;


void main()

{

    float rst;

    bitset<32> input;

    cout<<"本程序可以将用户输入的二进制码按照IEEE-754标准转换为小数。"<<endl;

    cout<<endl;

    while(1)

    {

        cout<<"请输入要转换的二进制码:"<<endl;

        cin>>input;

        rst= *(float *)&input;
            // 获取内存中保存的input的值并按浮点数格式表达

        cout<<"转换结果为:"<<endl;

        cout<<rst<<endl;

        cout<<endl;

    }

}

其运行截图为:

同样,只要把 float换成doublebitset<32>换成bitset<64>即可转换64位双精度浮点数。

 

注:对于浮点数,C++中的cout默认输出6位有效数字。

 

 

 

不过,学了Fortran90后,发现上面的程序用Fortran90可以写的更简单:


1、小数àà浮点数:

!-------------------------------------------

! 本程序将32位二进制浮点数编码转换为小数

! 请将要转换的数写在input.dat中,每行一个数

! 程序作者:孙晓博

! 创建时间:2012-09-07

!-------------------------------------------

program main

integer*4::num

open(1,file='input.dat')

! 输出结果保存在output.dat中

open(2,file='output.dat')

do

    read(1,"(B32)",end=100)
num

    write(2,"(B32.32,':
',ES45.39)") num,num

enddo

100 close(1)

close(2)

write(*,*)"Mission Accomplished!"

end program main

 

2、浮点数 àà 小数:

!-------------------------------------------

! 本程序将小数数转换为32位二进制浮点数编码

! 请将要转换的数写在input.dat中,每行一个数

! 程序作者:孙晓博

! 创建时间:2012-09-07

!-------------------------------------------

program main

real*4::num

open(1,file='input.dat')

! 输出结果保存在output.dat中

open(2,file='output.dat')

do

    read(1,*,end=100) num

    write(2,"(G,': ',B32.32)")
num,num

enddo

100 close(1)

close(2)

write(*,*)"Mission Accomplished!"

end program main

 

         以上针对的都是32位浮点数,若需要64位浮点数,只需将红色加粗部分的integer*4改为integer*8real*4改为real*8B32改为B64B32.32改为B64.64

【上篇】
【下篇】

抱歉!评论已关闭.