我有这段代码:
double A = doSomethingWonderful(); // same as doing A = 0;
if(A == 0)
{
fprintf(stderr,"A=%llx\n", A);
}
并输出:
A=7f35959a51c0
这怎么可能?
我检查了7f35959a51c0的值,看起来像是6.91040329973658785751176861252E-310,它非常小,但不是零。
编辑:
好的我明白那种打印双精度值的方法不起作用。我需要找到一种打印双精度字节的方法。
在评论之后我修改了我的代码:
A = doSomethingWonderful();// same as doing A = 0;
if(A == 0)
{
char bytes[8];
memcpy(bytes, &A, sizeof(double));
for(int i = 0; i < 8; i++)
fprintf(stderr," %x", bytes[i]);
}
我得到了这个输出:
0 0 0 0 0 0 0 0
所以最后似乎比较工作正常,但我做的不好。
答案 0 :(得分:6)
IEEE 754精度浮点值使用指数值中的偏差来完全表示正指数和负指数。对于双精度值,该偏差为1023 [source] ,恰好为十六进制0x3ff
,与您为其打印的A
的十六进制值匹配1
或0e0
。
另外两个小笔记:
%hhx
使其仅打印2个十六进制数字,而不是将符号扩展为8。double A = 0;
if(A == 0)
{
A = 1; // Note that you were setting A to 1 here!
char bytes[8];
memcpy(bytes, &A, sizeof(double));
for(int i = 0; i < 8; i++)
printf(" %hhx", bytes[i]);
}
int isZero;
union {
unsigned long i;
double d;
} u;
u.d = 0;
isZero = (u.d == 0.0);
printf("\n============\n");
printf("hex = %lx\nfloat = %f\nzero? %d\n", u.i, u.d, isZero);
结果:
0 0 0 0 0 0 f0 3f
============
hex = 0
float = 0.000000
zero? 1
所以在第一行中,我们看到1.0
是0e0
(即0 0 )。
在以下几行中,我们看到当您使用联合打印双0.0
的十六进制值时,您会按预期得到0
。
答案 1 :(得分:2)
将双精度值传递给printf()
时,将其作为浮点值传递。但是,由于"%x"
格式是整数格式,因此printf()
实现将尝试读取整数参数。由于这种基本类型不匹配,例如,调用代码可能将您的double值放在浮点寄存器中,而printf()
实现尝试从整数寄存器读取它。细节取决于您的ABI,但显然您看到的位不是您传递的位。从语言的角度来看,当一个printf()
参数与其相应的格式规范之间存在类型不匹配时,您就会有未定义的行为。
除此之外,+0.0
确实表示为所有位零,包括单精度和双精度格式。但是,这只是正零,-0.0
用符号位设置表示。
在最后一段代码中,您正在检查1.0
的位模式,因为在进行转换之前会覆盖A
的值。另请注意,由于符号扩展,您会在第七个字节中获得fffffff0
而不是f0
。要获得正确的输出,请使用unsigned
字节数组。
您看到的模式解码如下:
00 00 00 00 00 00 f0 3f
to big endian:
3f f0 00 00 00 00 00 00
decode fields:
sign: 0 (1 bit)
exponent: 01111111111 (11 bit), value = 1023
exponent = value - bias = 1023 - 1023 = 0
mantissa: 0...0 (52 bit), value with implicit leading 1 bit: 1.0000...
entire value: -1^0 * 2^0 * 1.0 = 1.0