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的规定来说,是必需的。
<think> 嗯,用户询问的是IEEE 754浮点数截取方法,这个需求很明确。从用户引用的资料来看,用户似乎混淆了标准名称,把"754"误写成了"745",这在技术文档中很常见。用户引用的四份材料都围绕IEEE 754标准展开,特别是引用[1]详细说明了单精度浮点数存储格式,引用[3]展示了C#的转换代码,这些都非常相关。 用户可能正在开发底层硬件或处理二进制数据转换,因为引用[1]提到Verilog实现,引用[3]涉及十六进制到浮点数的转换。考虑到引用[2]中用户提到"面试中被问懵了",ta可能是在准备技术面试,需要快速掌握核心概念。用户情绪似乎有些焦虑,从"坑死个人"这样的表述能感受到ta的挫败感。 关于截取操作,需要区分三个层面:首先是精度截断,比如双精度转单精度时尾数的取舍;其次是存储截断,就像引用[3]代码中通过字符串截取实现分段解析;最后是运算截断,引用[4]提到的舍入模式就属于这类。特别要注意阶码全0/全1的特殊值处理,这是容易出错的地方。 在整理回答时,应该从二进制结构切入,用引用[1]的格式说明作为基础。引用[3]的代码虽然有用,但用户需要的是方法原理而非具体实现,所以重点解释bit位操作规则就好。对于舍入机制,引用[4]的四种模式需要精炼说明,避免让用户陷入数学细节。 用户可能还需要知道截取后的误差影响,这在引用[2]提到"精度缺失"时有所暗示。不过ta没有明确要求数学证明,所以用单精度范围示例说明即可。最后考虑补充应用场景,因为引用[1][3]都涉及工程实现,硬件转换和数据分析的案例应该对用户有帮助。 </think> 根据IEEE 754标准(非"745"),浮点数截取主要涉及以下技术和方法,核心在于理解其二进制结构: --- ### **IEEE 754浮点数的截取方法** #### **1. 二进制结构解析(以32位单精度为例)** - **符号位(1位)**:最高位(bit 31),`0`表示正数,`1`表示负数。 - **阶码(8位)**:bit 23~30,**移码表示**(实际指数 = 阶码值 - 127)。 - **尾数(23位)**:bit 0~22,隐含整数位`1`(规范数)或`0`(非规范数),实际尾数 = `1.尾数`或`0.尾数`[^1]。 --- #### **2. 关键截取步骤** ##### **(1) 按字段截取二进制位** ```python # 示例:从32位二进制串中提取字段 binary_str = "11000010100011000000000000000000" # 示例浮点数二进制表示 sign_bit = binary_str[0] # 符号位 -> '1' exponent_bits = binary_str[1:9] # 阶码 -> '10000101' mantissa_bits = binary_str[9:32] # 尾数 -> '00011000000000000000000' ``` ##### **(2) 转换各字段为实际值** - **符号**:`sign = (-1)^int(sign_bit)` - **阶码**:`exponent = int(exponent_bits, 2) - 127` - **尾数**: - 若阶码 ≠ 0(规范数):`mantissa = 1 + sum(b * 2^(-i) for i,b in enumerate(mantissa_bits,1)` - 若阶码 = 0(非规范数):`mantissa = sum(...)`(无隐含`1`)[^1]。 --- #### **3. 截断操作的精度控制** ##### **(1) 尾数截断(Rounding)** 当浮点数运算结果尾数超出23位时,需按标准舍入: - **常用舍入模式**: - **最近偶数(Round to Nearest, ties to even)**:优先接近值,相等时向偶数舍入(默认模式)。 - **朝零舍入(Round towards zero)**:直接截断多余位(精度损失大)[^4]。 ##### **(2) 范围截断(Overflow/Underflow)** - **阶码溢出(>127)** → 返回`±Infinity`(正/负无穷)。 - **阶码下溢(<-126)** → 返回`0`或非规范数(逐级损失精度)[^1]。 --- #### **4. 特殊值处理** | 阶码 | 尾数 | 含义 | |-------------|------------|--------------| | 全`1`(255)| 全`0` | `±Infinity` | | 全`1`(255)| 非全`0` | `NaN` | | 全`0`(0) | 全`0` | `±0.0` | | 全`0`(0) | 非全`0` | **非规范数** | > ⚠️ 截取时需检查特殊值,避免常规计算[^1][^4]。 --- ### **典型应用场景** 1. **硬件实现(如FPGA)** Verilog中直接操作32位寄存器,按位域提取字段[^1]。 2. **数据通信协议** 接收十六进制数据流(如`40 49 0F DB`),按字节重组后解析[^3]。 3. **精度优化** 在AI推理中,将FP32模型截断为FP16或BF16以减少计算量。 --- ### **常见问题示例** 1. **十六进制`0x40490FDB`转为浮点数?** - 步骤: ```python hex_str = "40490FDB" binary = bin(int(hex_str, 16))[2:].zfill(32) # 补足32位 # 按上述方法解析符号、阶码、尾数 → 结果≈3.14159265(π) ``` 2. **尾数截断至10位如何影响精度?** - 损失13位精度(约十进制4~5位有效数字),误差范围约`10^{-4}`。 ---
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值