目前我需要忽略可移植性问题,并假设 long double 数据类型在每个架构上都是一致的 16 字节值,并且始终以 IEEE 754 四精度二进制浮点格式存储在内存中。这意味着最左边的位是符号位,后跟 15 位指数和另外 112 位二进制数据,以表示有效的十进制数字。当然不是十进制或 BCD,这都是二进制。
因此,数字 3 在内存中的表示如下所示:
(dbx) x &ld3 / 1 E
0xffffffff7ffff400: 0x40008000 0x00000000 0x00000000 0x00000000
由于 C 中的这一行,我从调试器中得到了它:
long double ld3 = 3.0;
在这里,我们看到前 16 位,即最左边的 16 位,是:
0x4000h = 0100 0000 0000 0000b
这里符号位为零,因此根据 IEEE754 规则表示正值,然后我们有 15 位的指数值 100 0000 0000 0000b。我花了一段时间阅读然后重新阅读以下文档:
http://en.wikipedia.org/wiki/Quadruple_precision
该指数按面值计算为 2^14,即 16,384。然而,(2^14) - 1 的“零偏移”值是 16,383,因此我上面的指数实际上只是 16,384 - 16,383 = 1。到目前为止好东西。
实际数字区域中的数据,接下来的 112 位是 0x8000 0000 ... 0000h 看起来是 1000 0000 ... 0000 似乎是错误的。三的二进制值应该是两位一,后跟一堆零。所以这让我很困扰。
所以我想编写一个代码位,它将一个长双精度变量打印为十六进制字节序列。但是,我遇到了实际获取这些字节作为变量地址的偏移量的问题。
我试试这个:
uint8_t j = 0;
j = ( (uint8_t *)(&ld3) + 2 );
fprintf ( stdout , " &ld3 = %p\n", &ld3 );
fprintf ( stdout , "byte 2 of ld3 = 0x%02x\n", j );
我看到这个:
&ld3 = ffffffff7ffff4b0
byte 2 of ld3 = 0xb2
我不知道它来自哪里,但它不能来自变量 ld3 占用的内存区域。
所以我希望有一个 for 循环遍历 long double 的 16 个字节,并在每个字节处打印出十六进制值,但我认为我在转换指针或地址或其他东西时遇到了严重错误。
所以我想问题是......在 long double 变量中获取特定字节的神奇 foo 是什么?
跟进:有了下面提供的建议,我可以展示这个工作案例,我们在最后两位看到一点噪音或错误。
#include <ctype.h>
#include <errno.h>
#include <math.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
long double two_ld = 2.0;
long double e64_ld;
uint8_t byt = 0, k;
e64_ld = expl(logl(two_ld)*(long double)64.0);
fprintf ( stdout , " sizeof (_ld) = %i\n", sizeof(e64_ld) );
fprintf ( stdout , " ( 2 ^ 64 ) = %36.32Le\n", e64_ld );
fprintf ( stdout , " = 0x" );
for ( k = 0; k<16; k++ )
fprintf ( stdout , "%02x ", * ( (uint8_t *)(&e64_ld) + k ) );
fprintf ( stdout , "\n" );
return ( EXIT_SUCCESS );
}
输出显示最右侧的位具有确实不应该存在的值:
sizeof (_ld) = 16
( 2 ^ 64 ) = 1.84467440737095516160000000000000e+19
= 0x40 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 02
不过,考虑到我使用对数计算了 2^64,这还不错。