十六进制与浮点数的互相转换
第一部分:
关于16进制浮点数
对于大小为32-bit的浮点数(32-bit为单精度,64-bit浮点数为双精度,80-bit为扩展精度浮点数),
1、其第31 bit为符号位,为0则表示正数,反之为复数,其读数值用s表示;
2、第30~23 bit为幂数,其读数值用e表示;
3、第22~0 bit共23 bit作为系数,视为二进制纯小数,假定该小数的十进制值为x;
则按照规定,该浮点数的值用十进制表示为:
= (-1)^s * (1 + x) * 2^(e - 127)
对于49E48E68来说,
1、其第31 bit为0,即s = 0
2、第30~23 bit依次为100 1001 1,读成十进制就是147,即e = 147。
3、第22~0 bit依次为110 0100 1000 1110 0110 1000,也就是二进制的纯小数0.110 0100 1000 1110 0110 1000,其十进制形式为0.78559589385986328125,即x = 0.78559589385986328125。
这样,该浮点数的十进制表示
= (-1)^s * (1 + x) * 2^(e - 127)
= (-1)^0 * (1+ 0.78559589385986328125) * 2^(147-127)
= 1872333
以上内容为IEEE的标准,现在有个问题,假设我有两台设备在通讯,一个设备向另外一个设备发送数据,这个数据是浮点数,那么我怎么来发送这个数据呢?怎么让接受方知道发送来的是个浮点数呢?而且发送数据要尽量短,最好还要固定长度,也许有人会怎么做:A发送数据3.7586给B,A先将这个浮点数乘以10000变成整型37586,然后将这个整数发送给B,这样使用的弊端是:1、B要事先知道A将这个数字放大了多少倍。2、A需要做一次浮点数的乘法运算,而B要做一次浮点数的除法运算,这对于单片机来说是个负担。3、这样使用会多占用发送数据。因为事先没人知道这个浮点数到底多大。
想了半天,觉得使用IEEE的浮点数规则来发送是最可靠的,因为任何浮点数都被表示成4个字节,这对发送和接收双方都是个好消息,只要双方都知道要进行浮点数的发送就可以了。而且IEEE格式浮点数的转换是机器内部执行的,我们不再需要任何的转换,不增加运算量,不增加代码量。
按照这个原则,编写了测试代码如下:
发送方A:
float fSend; //A需要发送的浮点数据
char chSend[4]; //发送缓冲,经过转换后的浮点数据,变成一个字符型数组。
//以下为转换
chSend[0] = *((char *)(&fSend));
chSend[1] = *((char *)(&fSend) + 1);
chSend[2] = *((char *)(&fSend) + 2);
chSend[3] = *((char *)(&fSend) + 3);
此时A就可以将这个数字发送给B了,B接收到的是4个字节表示的一个浮点数,但需要经过如下转换使用:
float fReceive; //接收到的浮点数
char chReceive[4];//接收缓冲。B将接收到的4个字节保存在这里。
//以下为转换
*((char *)(&fReceive)) = chReceive[0];
*((char *)(&fReceive) + 1) = chReceive[1];
*((char *)(&fReceive) + 2) = chReceive[2];
*((char *)(&fReceive) + 3) = chReceive[3];
好了,此时的B已经得到了一个浮点数fReceive;
//------以上内容转载自:
//---------------------------------------------------------------------------------------------------------
第二部分:
#include <stdio.h>
unsigned char pData[4]={0x66,0xE6,0xF0,0x42};
unsigned char buf[4];
float num;
float Char2Float(unsigned char *chReceive)
{
float fReceive;
*((char *)(&fReceive)) = chReceive[0];
*((char *)(&fReceive) + 1) = chReceive[1];
*((char *)(&fReceive) + 2) = chReceive[2];
*((char *)(&fReceive) + 3) = chReceive[3];
return fReceive;
}
void Float2Char (float fSend, unsigned char *chSend)
{
chSend[0] = *((char *)(&fSend));
chSend[1] = *((char *)(&fSend) + 1);
chSend[2] = *((char *)(&fSend) + 2);
chSend[3] = *((char *)(&fSend) + 3);
}
int main(void)
{
char i;
num = Char2Float(pData);
printf("%f\r\n",num);
Float2Char(num,buf);
for(i=0;i<4;i++)
{
printf("%x\r\n",buf[i]);
}
return 0;
}
运行结果
建议在函数中不要对输入变量的内存直接进行操作,可以先在函数中申请一个局部变量,对局部变量操作完成后,赋值给需要转换的变量。直接操作在单片机中可能造成HardFault()。
//---------------------------------------------------------------------------------------------------------
第三部分:
更新:
说明:我是在DSP上做数据处理遇到,浮点型存储格式,转换成十进制的浮点型参与运算。
尝试用上面 第一部分,第二部分的规则和代码,在DSP平台上数据处理结果不正确。
最终通过用union与struct,问题解决了。对于这一类问题:位操作。其实每个平台的库代码里有现成的例子。那就是每个平台的库中都有寄存器的定义。就是用union与struct,即可解决。
typedef union
{
float f_val;
struct
{
unsigned long datatail: 23;
unsigned long exponent : 8;
unsigned long sign : 1;
} bits;
} myfloat;
在处理函数中,做赋值即可:
val.bits.sign = data_sign;
val.bits.exponent = data_exp;
val.bits.datatail= data_tail;
fReceive = val.f_val; //返回 fReceive 是float型
return fReceive;