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" )
'
假设我们有一些 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" )
'