IEEE745浮点数格式

IEEE745浮点数格式
2008-06-05 14:37
昨天在分析一个二进制文件时发现里面的浮点数既不是BIG_ENDIAN也不是LITTLE_ENDIAN,顺序有点怪。迫不得已今天查了查IEEE745标准的浮点数格式,论述的最明白的在
http://www.moon-soft.com/program/bbs/readelite10020.htm
转贴如下
【 在 ilovezks (ilovezks) 的大作中提到: 】
: 我想求教一个问题:
:   c语言中FLOAT 是如何表示的?尾数,阶码是如何在32位上安排的,即哪几位是
: 尾数,哪几位是阶码,那一位是符号位。听说与CPU有关,是真的吗?

     在C++里,实数(float)是用四个字节即三十二位二进制位来存储的。其中有1位符号位,8位指数位和23位有效数字位。实际上有效数字位是24位,因为第一位有效数字总是“1”,不必存储。
     有效数字位是一个二进制纯小数。8位指数位中第一位是符号位,这符号位和一般的符号位不同,它用“1”代表正,用”0“代表负。整个实数的符号位用“1”代表负,“0”代表正。
     在这存储实数的四个字节中,将最高地址字节的最高位编号为31,最低地址字节的最低位编号为0,则实数各个部分在这32个二进制位中的分布是这样的:31 位是实数符号位,30位是指数符号位,29---23是指数位,22---0位是有效数字位。注意第一位有效数字是不出现在内存中的,它总是“1”。
    
     将一个实数转化为C++实数存储格式的步骤为:
     (1)先将这个实数的绝对值化为二进制格式,注意实数的整数部分和小数部分化为二进制的方法是不同的。
     (2)将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边。
     (3)从小数点右边第一位开始数出二十三位数字放入第22到第0位。
     (4)如果实数是正的,则在第31位放入“0”,否则放入“1”。
     (5)如果n 是左移得到的,说明指数是正的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”。
     (6)如果n是左移得到的,则将n减去一然后化为二进制,并在左边加“0”补足七位,放入第29到第23位。如果n是右移得到的或n=0,则将n化为二进制后在左边加“0”补足七位,再各位求反,再放入第29到第23位。
    
     将一个计算机里存储的实数格式转化为通常的十进制的格式的方法如下:
     (1)将第22位到第0位的二进制数写出来,在最左边补一位“1”,得到二十四位有效数字。将小数点点在最左边那个“1”的右边。
     (2)取出第29到第23位所表示的值n。当30位是“0”时将n各位求反。当30位是“1”时将n增1。
     (3)将小数点左移n位(当30位是“0”时)或右移n位(当30位是“1”时),得到一个二进制表示的实数。
     (4)将这个二进制实数化为十进制,并根据第31位是“0”还是“1”加上正号或负号即可。

     特别地,实数0用C++的float格式表示是00000000000000000000000000000000。
    
     如果还不太明白,这里举几个例子。
     一。将23.56化为C++的float格式。
     (1)将23.56化为二进制后大约是“10111.1000111101011100001”。
     (2)将小数点左移四位,得到“1.01111000111101011100001”。
     (3)这已经有了二十四位有效数字,将最左边一位“1”去掉,得到“01111000111101011100001”。将它放入第22到第0位。
     (4)因为23.56是正数,因此在第31位放入“1”。
     (5)由于我们把小数点左移,因此在第30位放入“1”。
     (6)因为我们是把小数点左移4位,因此将4减去1得3,化为二进制,并补足七位得到0000011,放入第29到第23位。
     完毕。
     如果把最左边定为第31位,最右边定为第0位,那么在C++里,float格式的23.56是这样表示的:01000001101111000111101011100001。相应地-23.56就是这样表示的:11000001101111000111101011100001。

     二。将实数0.2356化为C++的float格式。
     (1)将0.2356化为二进制后大约是0.00111100010100000100100000。
     (2)将小数点右移三位得到1.11100010100000100100000。
     (3)从小数点右边数出二十三位有效数字,即11100010100000100100000放入第22到第0位。
     (4)由于0.2356是正的,所以在第31位放入“0”。
     (5)由于我们把小数点右移了,所以在第30位放入“0”。
     (6)因为小数点被右移了3位,所以将3化为二进制,在左边补“0”补足七位,得到0000011,各位取反,得到1111100,放入第29到第23位。
     完毕。因此0.2356用C++的float格式表示是:00111110011100010100000100100000。其中最左边一位是第31位,最右边一位是第0位。

     三。将实数1.0化为C++的float格式。
     (1)将1.0化为二进制后是1.00000000000000000000000。
     (2)这时不用移动小数点了,这就是我们在转化方法里说的n=0的情况。
     (3)将小数点右边的二十三位有效数字00000000000000000000000放入第22到第0位。
     (4)因为1.0是正的,所以在第31位里放入“0”。
     (5)因为n=0,所以在第30位里放入“0”。
     (6)因为n=0,所以将0补足七位得到0000000,各位求反得到1111111,放入第29到第23位。
     完毕。所以实数1.0用C++的float格式表示是:00111111100000000000000000000000。其中最左边一位是第31位,最右边一位是第0位。
这是IEEE短实数格式,适合X86cpu。


转换的C程序参考
http://www.x5dj.com/blog/00148315/00132755.shtml
要实现转换,实际上就是使用不同类型的指针来读取内存中的特定单元长度的数据
这里使用了 指针类型的强制转换:(float *)sp_ch和:(char*)sp_f

下面提供了一个标准C的函数,实现互换:

#include "stdio.h"
void main(void)
{
float a=2.584;
/*2.584的IEEE754格式为0x40256042*/
char fchar[4];
char *ptr_c;
float *ptr_f;

fchar[3]=0x40;
fchar[2]=0x25;
fchar[1]=0x60;
fchar[0]=0x42;
 
/*高位内存的数据,因为存放时候要注意顺序*/

ptr_c=(char *)&a;
printf("Addr of Float:%p/nAddr of Float Plus 1:%p/n",&a,&a+1);
printf("%f:0x%x%x%x%x/n",a,*(ptr_c+3),*(ptr_c+2),*(ptr_c+1),*ptr_c);
/*高位在高位,低位在低位*/

printf("%f:%p/n",*((float *)(fchar)),(float *)(fchar));
}

单独的函数实现

float IEEE754_Conv(unsigned char in_char[])
/*2.584:0x40256042*/
{
unsigned char tmp_char[4];
uchar msg[20];
tmp_char[0]=in_char[3];
tmp_char[1]=in_char[2];
tmp_char[2]=in_char[1];
tmp_char[3]=in_char[0];
sprintf(msg,"%.3f",*((float *)tmp_char));
return ((float)atof(msg));
}

其它:浮点数有两个例外。数0.0存储为全零。无限大数的阶码存储为全1,尾数部分全零。符号位指示正无穷或者负无穷。

理论参考:基于IEEE 754的浮点数存储格式分析研究 http://www.studa.net/yingyong/080502/15414593.html

双精度格式
    IEEE双精度浮点格式共64位,占2个连续32位字,包含三个构成字段:52位的小数f,11位的偏置指数e,1位的符号位s。将这2个连续的32位字整体作为一个64位的字,进行重新编号。其中0:51位包含52位的小数f;52:62位包含11位的偏置指数e;而最高位,第63位包含符号位s。
f[31:0]存放小数f的低32位,其中第0位存放整个小数f的最低有效位LSB,第31位存放小数f的低32位的最高有效位MSB。
    在另外的32位的字里,第0 到19位,即f[51:32],存放小数f的最高的20位,其中第0位存放这20位最高有效数中的最低有效位LSB,第19位存放整个小数f的最高有效位 MSB。第20到30位,即e[52:62],存放11位的偏置指数e,其中第20位存放偏置指数的最低有效位LSB,第30位存放最高有效位MSB。最高位,第31位存放符号位s。
    在Intel x86结构计算机中,数据存放采用小端法(little endian),故较低地址的32位的字中存放小数f的f[31:0]位。而在在SPARC结构计算机中,因其数据存放采用大端法(big endian),故较高地址的32位字中存放小数f的f[31:0]位。

(1)扩展双精度格式(SPARC 结构计算机)
    该4倍精度浮点环境符合IEEE关于扩展双精度格式的定义。该浮点环境的4倍精度浮点格式共128位,占4个连续32位字,包含3个构成字段:
112位的小数f,15位的偏置指数e,和1位的符号s。将这4个连续的32位字整体作为一个128位的字,进行重新编号。其中0:110位包含小数f;112: 126位包含偏置指数e;第127位包含符号位s。如图3所示。
在SPARC结构计算机中,地址最高的32位字存放小数的32位最低有效位,即f[31:0];但是在PowerPC结构计算机中,却是地址最低的32位字存放这些位。
紧邻的两个32位字(在SPARC机中向下计算,在PowerPC机中向上计算)分别存放f[63:32]和f[95:64]。
    最后一个字的第0到15位存放小数的最高16位,即f[111:96]。其中第0位存放该16位的最低有效位,第15位存放整个小数f的最高有效位。第 16到30位存放15位的偏置指数e,其中第16位存放偏置指数的最低有效位,第30位存放它的最高有效位。最高位,第31位存放符号s。

(2)扩展双精度格式(Intel x86结构计算机)
    该浮点环境双精度扩展格式符合IEEE双精度扩展格式的定义。该浮点环境的扩展双精度格式共80位,占3个连续32位字,包含四个构成字段:63位的小数 f,1位显式前导有效位(explicit leading significand bit)j,15位偏置指数e,和1位符号位s。将这3个连续的32位字整体作为一个96位的字,进行重新编号。其中0:63包含63位的小数f,第63 位包含前导有效位j,64:78位包含15位的偏置指数e,最高位第79位包含符号位s。
    在Intel结构系计算机中,这些字段依次存放在十个连续的字节中。但是,由于 UNIX System V Application Binary Interface Intel 386 Processor Supplement (Intel ABI) 要求双精度扩展参数,从而占用堆栈中3个相连地址的32位字,其中最高一个字的高16位未被使用。
    地址最低的32位字存放小数f的低32位,即f[31:0]。其中第0位存放整个小数f的最低有效位LSB 第31位存放小数低32位的最高有效位MSB。
    地址居中的32位字,第0到30位存放小数f的31位最高位,即f[62:32]。其中第0位存放31位最高小数位的最低有效位LSB,第30位存放整个小数的最高有效位,地址居中的32位字的最高位第31位存放显式的前导有效位j。
    地址最高32位字里,第0到14位存放15位的偏置指数e,第0位存放偏置指数的最低有效位LSB,第14位存放最高有效位MSB,第15位存放符号位 s。虽然地址最高的32位字的高16位在Intel x86结构系列机种未被使用,但他们对符合Intel ABI的规定来说,是必需的。
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值