做过网络编程的程序员们,都知道在进行二进制数据传输时,绕不开所谓“大小端”的问题(如果不清楚,请自行百度下哈)。之前以为只有Integer才会做大小端转换,今天发现其实对于多字节的数据,比如单精度/双精度浮点数,其实也有大小端的考虑。下面以单精度浮点数为例说明
根据二进制浮点数算术标准(IEEE754),32位单精度浮点数的二进制表示如下图所示(左端为MSB高位,右端为LSB低位)
所以现在的问题是,
- 在计算机内存中,低地址存放的是最右边的fraction(小数位部分),还是最左边的sign(符号)和exponent(指数)部分呢?
- 在传输一个单精度浮点数时,我们应该先传输最右边的fraction(小数位部分),还是最左边的sign(符号)和exponent(指数)部分呢?
答案是这些和处理整数的时候一样,要区分大小端。比如在我的Intel处理器(默认小端)的PC上,下面一段程序
#include <iostream>
void PrintBytes(float n) {
auto ch = reinterpret_cast<uint8_t*>(&n);
// 从低地址开始打印,每次一个字节
for (int i = 0; i < sizeof(n); ++i) {
std::cout << std::showbase << std::hex << static_cast<int>(*(ch + i)) << std::endl;
}
}
int main() {
PrintBytes(2.0);
return 0;
}
运行后打印的结果是
对于单精度浮点数2.0,按照IEEE标准,32比特的二进制形式如下(这里的指数部分100000000等于十进制的128,表示数值1,因为指数部分为了表示正、负幂,用127表示数值0,更多详情请参考其他资料)
符号(MSB) | 指数 | 小数(LSB) |
---|---|---|
0 | 10000000 | 00000000000000000000000 |
所以可以看到,先打印的是低比特位(LSB)的小数部分,后打印的符号+指数高比特位(MSB)部分,也就是说Intel处理器在低地址存放浮点数的低位,高地址存放浮点数的高位。等等,小端的定义——在低地址存放低bit位,高地址存放高比特位——不正是这个吗?正是如此。所以结论就是,在Intel处理器中浮点数也是按照小端存储的。在网络传输浮点数时,也需要考虑大小端。