天天看点

十六进制与浮点数的互相转换

十六进制与浮点数的互相转换

第一部分:

关于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;

//------以上内容转载自:https://blog.csdn.net/educast/article/details/8522818

//---------------------------------------------------------------------------------------------------------

第二部分:

#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()。

//------以上内容转载自:https://blog.csdn.net/qq_38158680/article/details/98075213

//---------------------------------------------------------------------------------------------------------

第三部分:

更新:

说明:我是在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;
           

继续阅读