我在我的 x86 VM(32 位)上发现以下程序:
#include <stdio.h>
void foo (long double x) {
int y = x;
printf("(int)%Lf = %d\n", x, y);
}
int main () {
foo(.9999999999999999999728949456878623891498136799780L);
foo(.999999999999999999972894945687862389149813679978L);
return 0;
}
产生以下输出:
(int)1.000000 = 1
(int)1.000000 = 0
Ideone 也会产生这种行为。 http://ideone.com/tOJmNb
编译器做了什么来允许这种情况发生?
当我追踪为什么以下程序没有生成时,我发现了这个常数0
正如我所料(使用 199
s 产生了0
我期望):
int main () {
long double x = .99999999999999999999L; /* 20 9's */
int y = x;
printf("%d\n", y);
return 0;
}
当我尝试计算结果从预期切换到意外的值时,我得出了这个问题所涉及的常数。
你的问题是long double
您的平台上的精度不足以存储精确值 0.99999999999999999999。这意味着它的值必须转换为可表示的值(此转换发生在程序翻译期间,而不是在运行时)。
该转换可以生成最接近的可表示值,或者下一个更大或更小的可表示值。该选择是实现定义的,因此您的实现应该记录它正在使用哪个。看来你的实现使用了 x87 风格的 80 位long double
,并且四舍五入到最接近的值,导致值 1.0 存储在x
.
假设格式为long double
(有 64 个尾数位),小于 1.0 的最高可表示数字是,以十六进制表示:
0x0.ffffffffffffffff
该值与下一个更高的可表示数字 (1.0) 之间的正中间数字是:
0x0.ffffffffffffffff8
你的非常长的常量 0.9999999999999999999728949456878623891498136799780 等于:
0x0.ffffffffffffffff7fffffffffffffffffffffffa1eb2f0b64cf31c113a8ec...
如果舍入到最接近的值,显然应该向下舍入,但您似乎已经达到了编译器正在使用的浮点表示的某些限制,或者是舍入错误。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)