128 位浮点二进制表示错误

128 bit floating point binary representation error

假设我们有一些 128 位浮点数,例如 x = 2.6 (1.3 * 2^1 ieee-754)。 我像这样放在联合中:

union flt {
        long double flt;
        int64_t byte8[OCTALC];
    } d;
d = x;

然后我运行这样得到它在内存中的十六进制表示:

void print_bytes(void *ptr, int size) 
{
    unsigned char *p = ptr;
    int i;
    for (i=0; i<size; i++) {
        printf("%02hhX ", p[i]);
    }
    printf("\n");
}

// some where in the code
print_bytes(&d.byte8[0], 16);

我得到了类似的东西

66 66 66 66 66 66 66 A6 00 40 00 00 00 00 00 00

因此,根据假设,我希望看到一个前导位(左边的位)为 1(因为 2.6 的指数为 1)但实际上我看到右边的位为 1(就像它处理值大-端)。如果我翻转签名,输出更改为:

66 66 66 66 66 66 66 A6 00 C0 00 00 00 00 00 00

看来符号位比我想象的要正确。而且,如果您计算字节数,似乎只使用了 10 个字节,剩下的 6 个字节就像 t运行cated 之类的。 我试图找出发生这种情况的原因有什么帮助吗?

您被某些 非常 奇怪的方面所迷惑 extended-precision floating-point 通常在 Intel 体系结构上用 C 实现。所以不要觉得太糟糕。 :-)

您看到的是,虽然 sizeof(long double) 可能是 16(== 128 位),但在您内心深处真正得到的是 80-bit Intel extended format。它被填充了 6 个字节,在你的情况下恰好是 0。所以,是的,“符号位比你想象的要正确”。

我在我的机器上看到了同样的东西,这是我一直想知道的事情。这看起来真的很浪费,不是吗?我曾经认为这是为了与实际上具有 128 位长双精度的机器的某种兼容性。但这不可能,因为这个 0 填充的 16 字节格式是 而不是 binary-compatible 和 true IEEE 128-bit floating point,除此之外,因为填充在错误的结局。

你有很多误解。

首先,你没有128位的浮点数。 long double 可能是 x86-64 上 x86 extended precision format 中的一个浮点数。这是一个 80 位(10 字节)的值,被填充为 16 字节。 (我怀疑这是为了对齐目的。)

当然,它的顺序是 little-endian byte(因为这是 x86/x86-64)。这里不是指每个字节中的位顺序,而是指字节在整体中的顺序。

最后,指数是有偏差的。 1 的指数不存储为 1。它存储为 1+0x3FFF。这允许负指数。


所以我们得到以下内容:

66 66 66 66 66 66 66 A6 00 40 00 00 00 00 00 00

Demo 在编译器资源管理器上

如果我们删除填充并反转字节以更好地匹配维基百科页面中的图像,我们得到

4000A666666666666666

这转化为

+0x1.4CCCCCCCCCCCCCCC × 2^(0x4000-0x3FFF)

(0xA66...6 = 0b1010 0110 0110...0110 ⇒ 0b1.0100 1100 1100...110[0] = 0x1.4CC...C)

+1.29999999999999999995663191310057982263970188796520233154296875 × 2^1

使用

获得的十进制转换
perl -Mv5.10 -e'
   use Math::BigFloat;
   Math::BigFloat->div_scale( 1000 );
   say
      Math::BigFloat->from_hex(  "4CCCCCCCCCCCCCCC" ) /
      Math::BigFloat->from_hex( "10000000000000000" )
'

perl -Mv5.10 -e'
   use Math::BigFloat;
   Math::BigFloat->div_scale( 1000 );
   say
      Math::BigFloat->from_hex( "A666666666666666" ) /
      Math::BigFloat->from_hex( "8000000000000000" )
'