讨论之前说明:
或许你觉得这个论题没有意义,因为完全可以把浮点数扩大相应的倍数,把浮点数变成整数再传输,解析的时候,直接把它们缩小相应的倍数就可以了。我也觉得这个方法是最好的,简单方便,但前提是需要工程人员们开发时协商好,或者利用文档做好约束说明。
可有的时候,并没有把浮点数经过任何处理,直接把它们按照计算机存储浮点数特有的方式(二进制)发送出来了,这时候需要把浮点数按照计算机存储浮点数的形式解析出来。由此就有个疑问:如何快速的把这些二进制数解析成浮点数呢?
1.浮点数在内存中存储形式
任何数据在内存中都是以二进制的形式存储的。对于浮点数在内存是如何用二进制存储的?目前所有的C/C++编译器都是采用IEEE所制定的标准浮点格式,即二进制科学表示法。
二进制科学表示法与十进制科学表示法类似:
- 十进制科学表示法: a×10b (1<|a|<10,b是整数,a和b是十进制数)
- 二级制科学表示法: a×2b (1≤|a|<2,b是整数,a和b是二进制数)
如:
-8.25用十进制的科学表示法表示就为:
−8.25×101
-8.25用二进制的科学表示法表示就为:
−1.0001×103
(具体方法见这)
根据上述理论,可看出无论是单精度还是双精度在存储中只需要为三个部分:
- 符号位(Sign) : 0代表正,1代表为负
- 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储
- 尾数部分(Mantissa):尾数部分
char:1个字节
short:2个字节
int:4字节
long:4字节
float:4字节(单精度)
double:8字节(双精度)
IEEE浮点数规定:
- 对于float型数据,其二进制有32位,其中符号位1位,阶码8位,尾数23位;
- 对于double型数据,其二进制为64位,符号位1位,阶码11位,尾数52位。
类型 | 31 | 30-23 | 22-0 |
---|---|---|---|
float | 符号位 | 阶码 | 尾数 |
类型 | 63 | 62-52 | 51-0 |
---|---|---|---|
double | 符号位 | 阶码 | 尾数 |
- 符号位:0表示正,1表示负
- 阶码:这里阶码采用移码表示,对于float型数据其规定偏置量为127,阶码有正有负,对于8位二进制,则其表示范围为-128-127,double型规定为1023,其表示范围为-1024-1023。比如对于float型数据,若阶码的真实值为2,则加上127后为129,其阶码表示形式为10000010
- 尾数:有效数字位,即部分二进制位(小数点后面的二进制位),因为规定a的整数部分恒为1,所以这个1就不进行存储了。
下面举例说明:
float型数据125.5转换为标准浮点格式
125二进制表示形式为1111101,小数部分表示为二进制为1,则125.5二进制表示为1111101.1,由于规定尾数的整数部分恒为1,则表示为1.1111011*2^6,阶码为6,加上127为133,则表示为10000101,而对于尾数将整数部分1去掉,为1111011,在其后面补0使其位数达到23位,则为11110110000000000000000
则其二进制表示形式为
0 10000101 11110110000000000000000
而反过来若要根据二进制形式求算浮点数
如0 10000101 11110110000000000000000,由于符号为为0,则为正数。阶码为133-127=6,尾数为11110110000000000000000,则其真实尾数为1.1111011。所以其大小为1.1111011*2^6,将小数点右移6位,得到1111101.1,而1111101的十进制为125,0.1的十进制为
1×2^(-1)=0.5,所以其大小为125.5。
由于以上不是重点讨论的知识,所以如果对以上讲解不是很清楚的,可以参见浮点在内存中的存储这里
华丽的分割线—–我的重点讨论区
2.浮点数据在嵌入式串行通讯中的快速处理
根据浮点数在内存中的存储形式,在串行通讯中,我们自然想到可以根据浮点数在内存中占用的字节数,把它们按一个个字节发送出来,但是发送出来的字节我们又怎么快速的还原成浮点数呢?或许你会想,我可以利用浮点数在内存中存储形式,把它们的符号位、阶码、尾数一一求出来,然后组合成浮点数。
上述方法当然是可行的,可是都需要计算的过程。
那么我们想,是不是不用计算,就直接可以把多个字节解析成浮点数呢?答案当然是有的。其实原理很简单,就借助浮点数在计算机内存中存储的形式,把将原浮点数的字节按照相应的顺序赋值给新的浮点数。
因为需要把浮点数按照字节划分,所以我们很容易想到利用位域和共用体去实现整个过程。下面用float为例说明该过程:
#include <stdio.h>
typedef union
{
float all;
struct
{
unsigned char f1:8;
unsigned char f2:8;
unsigned char f3:8;
unsigned char f4:8;
}bit;
} FLOAT;
int main()
{
float a = 125.5;
FLOAT b;
b.all= a;
unsigned char b1 = 0, b2 = 0, b3 = 0, b4 = 0;
b1 = b.bit.f1;
b2 = b.bit.f2;
b3 = b.bit.f3;
b4 = b.bit.f4;/*把b1,b2,b3,b4发出去*/
/*解析接受到数据的过程*/
FLOAT c;
c.bit.f1 = b1;
c.bit.f2 = b2;
c.bit.f3 = b3;
c.bit.f4 = b4;
printf("c=%f", c.all);
return 0;
}
最后的结果:c=125.5
只需要这样简单的数据处理,就可以把浮点数采用二进制数的方式发送,并利用浮点数在计算机内存中以二进制固定格式存储的形式快速的解析出来。