为什么这种与零的比较不能正常工作?
Why this comparison to zero is not working properly?
我有这个代码:
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
最后看来比较工作正常,但我的打印效果不佳。
IEEE 754 精度浮点值在指数值中使用偏差,以便完全表示正指数和负指数。对于双精度值,偏差是 1023[source],它恰好是十六进制的 0x3ff
,它与 A
的十六进制值相匹配您为 1
或 0e0
.
打印
另外两个小笔记:
- 当打印字节时,你可以使用
%hhx
让它只打印 2 个十六进制数字而不是符号扩展到 8。
- 您可以使用联合将 double 值可靠地打印为 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
(即 00)。
在下面几行中,我们看到当您使用联合打印双精度 0.0
的十六进制值时,您会按预期得到 0
。
当您将双精度值传递给 printf()
时,您将其作为 浮点数 值传递。但是,由于 "%x"
格式是整数格式,您的 printf()
实现将尝试读取 integer 参数。由于这种基本类型不匹配,例如,调用代码可能将您的双精度值放在浮点寄存器中,而 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
我有这个代码:
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
最后看来比较工作正常,但我的打印效果不佳。
IEEE 754 精度浮点值在指数值中使用偏差,以便完全表示正指数和负指数。对于双精度值,偏差是 1023[source],它恰好是十六进制的 0x3ff
,它与 A
的十六进制值相匹配您为 1
或 0e0
.
另外两个小笔记:
- 当打印字节时,你可以使用
%hhx
让它只打印 2 个十六进制数字而不是符号扩展到 8。 - 您可以使用联合将 double 值可靠地打印为 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
(即 00)。
在下面几行中,我们看到当您使用联合打印双精度 0.0
的十六进制值时,您会按预期得到 0
。
当您将双精度值传递给 printf()
时,您将其作为 浮点数 值传递。但是,由于 "%x"
格式是整数格式,您的 printf()
实现将尝试读取 integer 参数。由于这种基本类型不匹配,例如,调用代码可能将您的双精度值放在浮点寄存器中,而 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