问题背景如下,在某些串口通信过程中会得到类似于下图所示的数据,数据格式为每四个十六进制数为一组,表示一个浮点数,在下图所示的数据中每一行包含三个浮点数,最后多出来一个用于校验。其中每个浮点数为左边低字节右边高字节。
首先想说明的一点是,这个问题当然可以通过数学计算的方式解决。在计算机内存中,一个浮点数(float类型,32位)由四个字节表示,最高位表示符号,接下来8位表示阶码,然后23位表示尾数。例如上图中的第一个浮点数,按照从高位到低位的顺序写为BF 7D A0 C8,写成二进制形式为10111111 01111101 10100000 11001000;最高位为1,表示为负数;阶码部分为01111110,转换成十进制表示为126,去掉偏移量127,实际值为-1;尾数部分为1111101 10100000 11001000,根据指数部分为-1,浮点数实际数值的二进制表示为1.1111101 10100000 11001000,即0.11111101 10100000 11001000,转换成十进制表示为0.99073457717895507813。具体关于浮点数的表示,以及相关的数制转换可以查阅相关的资料和博客。
然而,图中给出的数据格式与浮点数在内存中的表示是一致的,也就是说,如果将内存中的连续四个字节填充为上述的数据,例如BF 7D A0 C8,然后将这四个字节整体当作浮点数来读取,理应得到相应的结果0.99073457717895507813。然而此处需要特别注意的是数据类型在实际计算机中的表示方式,一定要是四个字节才行。在Visual C++中float类型是用四个字节来表示的,所以可以这样操作。下面列举两种具体操作方式。
1 通过指针进行转换
由于在C/C++中char类型是用一个字节表示的,所以用一个包含4个元素的char类型的数组,可以表示内存中的连续4个字节。将4个字节分别填充为对应的数据后,用一个float类型的指针来读取整体相应的内容。
unsigned char s[4];
float *p;
float num;
s[0]=0xC8;
s[1]=0xA0;
s[2]=0x7D;
s[3]=0xBF;
p=(float*)s;
num=*p;
cout <<"num = "<< num << endl;
将变量num的值输出结果如下图,可以得到正确的结果。
2 位域
位域(Bit Field)是一种比较稍微特殊一点的方式,可以对一个变量的各个位进行操作,但是需要特别注意位域的分配方式,因为位域在内存中的对齐方式不一定如我们所想的那样,关于位域的具体介绍可以参考维基百科和Microsoft的文档,在对一些DSP进行编程时经常会用到位域的方式,如TI的TMS320F2812的代码框架就对DSP的各个寄存器定义了很多位域。由于此处正好是将一个浮点数划分为4个字节,而字节在内存中可以很方便地对齐,所以使用位域的方式来处理也是可以的。
具体的实现方式是采用struct类型定义位域,然后采用union类型和一个变量进行绑定。
typedef unsigned int Bit; //位域的类型必须是整型或枚举整型,通常用无符号整型
struct BitField //采用结构体来划分位域,每个位域后面用冒号和数字表示位域所占的位数,
{ //此处定义了F1,F2,F3,F4四个位域,所有位域均占8位,一共32位。
Bit F1:8;
Bit F2:8;
Bit F3:8;
Bit F4:8;
};
union BitFieldForFloat //采用联合体来将位域和float变量进行绑定,
{ //因为union类型的各个成员共用同一块内存区域。
struct BitField bitfield;
float value;
};
定义好了位域之后,就可以采用位域来进行数据的转换了。
union BitFieldForFloat num;
num.value=0;
num.bitfield.F1=0xC8;
num.bitfield.F2=0xA0;
num.bitfield.F3=0x7D;
num.bitfield.F4=0xBF;
cout<<"the value of the float number is "<<num.value<<endl;
程序输出结果如下图,可以得到正确的结果。
最后说明一下,其实位域更多的是用来对一个字节内的各个部分进行操作的,比如可以将一个字节(共8位)划分为3,3,2三个部分,然后对一个char类型的变量进操作,如下所示。当然,位域的划分也可以跨越多个字节。
typedef unsigned int Bit;
struct BitField
{
Bit bit_0_to_2:3;
Bit bit_3_to_5:3;
Bit bit_6_to_7:2;
}
union BitFieldForChar
{
struct BitField bitfield;
char value;
}
union BitFieldForChar number;
参考链接
16进制转浮点数的一个在线计算器