获得 long double 的二进制表示的正确方法是什么?

问题描述

这是我的尝试:

#include <iostream>

union newType {
        long double firstPart;
        unsigned char secondPart[sizeof(firstPart)];
} lDouble;

int main() {
    lDouble.firstPart = -16.5;

    for (int_fast16_t i { sizeof(lDouble) - 1 }; i >= 0; --i)
        std::cout << (int)lDouble.secondPart[i] << " ";

    return 0;
}
Output:  0 0 0 0 0 0 192 3 132 0 0 0 0 0 0 0  
Hex:     0 0 0 0 0 0  c0 3  84 0 0 0 0 0 0 0

我几乎同意“c0 3 84”部分,即“1100 0000 0000 0011 1000 0100”。

-16.5 = -1.03125 * 2^4 = (-1 + (-0.5) * 2^-4) * 2^4
Thus,the 117th bit of my fraction part must be 1 and after 5th division I'll get only "0".

sign(-):       1  
exponent(2^4): 4 + 16383 = 16387 = 100 0000 0000 0011  
fraction:      0000 1000 and 104 '0'

Result:   1| 100 0000 0000 0011| 0000 1000 and 104 '0'
Hex:           c    0    0    3     0    8 and 26 '0'

Or: c0 3 8 0 0 0 0 0 0 0 0 0 0 0 0 0

我不明白两件事:

  1. "c0 3 84" - 我在计算中哪里丢失了 4?我的猜测是它以某种方式存储 1(113 位)并且不应该存储它。然后是 1000 0100 而不是 0000 1000(在“c0 3”之后),正好是“84”。但是我们总是存储 112 位,而 1 总是隐式的。
  2. 为什么我的输出不是从 192 开始?为什么从0开始?我认为第一位是符号位,然后是指数(15 位)和分数(112 位)。

我已经设法表示其他数据类型(double、float、unsigned char 等)。使用 double 我采用了类似的方法并得到了预期的结果(例如 double -16.5 输出 192 48 128 0 0 0 0 0,或 c0 30 80 0 0 0 0 0)。

当然,我已经测试了 How to print binary representation of a long double as in computer memory?

的解决方案
Values for my -16.5 are: 0 0 0 0 0 0 0 0x84 0x3 0xc0 0xe2 0x71 0xf 0x56 0 0  
If I revert this I get:  0 0 56 f 71 e2 c0 3 84 0 0 0 0 0 0 0

而且我不明白为什么(再次)序列不是从符号位开始,那些“56 f 71 e2 c0”是什么?他们来自哪里?为什么(再次)在“8”之后有“4”?

解决方法

获取 long double 的二进制表示的正确方法是什么?

与获取任何平凡类型的二进制表示的方式相同。重新解释为 unsigned char 数组,并迭代每个字节是典型且定义明确的解决方案。

std::bitset 有助于二进制表示:

long double ld = -16.5;
unsigned char* it = reinterpret_cast<unsigned char*>(&ld);
for (std::size_t i = 0; i < sizeof(ld); i++) {
    std::cout
        << "byte "
        << i
        << '\t'
        << std::bitset<CHAR_BIT>(it[i])
        << '\t'
        << std::hex << int(it[i])
        << '\t'
        << std::dec << int(it[i])
        << '\n';
}

某些系统上的示例输出:

byte 0  00000000    0   0
byte 1  00000000    0   0
byte 2  00000000    0   0
byte 3  00000000    0   0
byte 4  00000000    0   0
byte 5  00000000    0   0
byte 6  00000000    0   0
byte 7  10000100    84  132
byte 8  00000011    3   3
byte 9  11000000    c0  192
byte 10 01000000    40  64
byte 11 00000000    0   0
byte 12 00000000    0   0
byte 13 00000000    0   0
byte 14 00000000    0   0
byte 15 00000000    0   0

请注意,由于读取了联合的非活动成员,您的示例在 C++ 中具有未定义的行为。


为什么我的输出不是从 192 开始的?

可能是因为末尾的那些字节恰好是填充。

为什么从0开始?

因为填充中包含垃圾。

我认为第一位是符号位,然后是指数(15 位)和小数(112 位)。

与其说是“第一个”位,不如说是“最重要”位,不包括填充。显然,您错误地假设了位数,因为其中一些用于填充。

请注意,C++ 不保证浮点表示是 IEEE-754,实际上,long double 通常不是 128 位“四重”精度浮点数,而是 80 位“扩展”精度浮点数。例如,x86 CPU 架构系列就是这种情况。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...