为什么在浮点文字的末尾添加 0 会改变它的舍入方式(可能是 GCC 错误)?
我在我的 x86 VM(32 位)上发现了以下程序:
I discovered on my x86 VM (32 bit) that the following program:
#include <stdio.h>
void foo (long double x) {
int y = x;
printf("(int)%Lf = %d
", x, y);
}
int main () {
foo(.9999999999999999999728949456878623891498136799780L);
foo(.999999999999999999972894945687862389149813679978L);
return 0;
}
产生以下输出:
(int)1.000000 = 1
(int)1.000000 = 0
Ideone 也会产生这种行为.
编译器做了什么来允许这种情况发生?
What is the compiler doing to allow this to happen?
我在追查为什么下面的程序没有像我预期的那样产生 0
时发现了这个常量(使用 19 个 9
产生了 0
我预计):
I found this constant as I was tracking down why the following program didn't produce 0
as I expected (using 19 9
s produced the 0
I expected):
int main () {
long double x = .99999999999999999999L; /* 20 9's */
int y = x;
printf("%d
", y);
return 0;
}
当我试图计算结果从预期变为意外时的值时,我得出了这个问题所涉及的常数.
As I tried to compute the value at which the result switches from expected to unexpected, I arrived at the constant this question is about.
推荐答案
您的问题是您的平台上的 long double
精度不足以存储精确值 0.99999999999999999999.这意味着必须将它的值转换为可表示的值(这种转换发生在程序的翻译过程中,而不是在运行时).
Your problem is that long double
on your platform has insufficient precision to store the exact value 0.99999999999999999999. This means that the value of that must be converted to a representable value (this conversion happens during translation of your program, not at runtime).
这种转换可以生成最接近的可表示值,也可以生成下一个更大或更小的可表示值.选择是实现定义的,因此您的实现应该记录它正在使用的.您的实现似乎使用 x87 样式的 80 位 long double
,并且正在四舍五入到最接近的值,导致 x
中存储的值为 1.0.
This conversion can generate either the nearest representable value, or the next greater or smaller representable value. The choice is implementation-defined, so your implementation should document which it is using. It seems that your implementation uses x87-style 80bit long double
, and is rounding to the nearest value, resulting in a value of 1.0 stored in x
.
使用 long double
的假定格式(尾数为 64 位),小于 1.0 的最高可表示数字是十六进制:
With the assumed format for long double
(with 64 mantissa bits), the highest representable number less than 1.0 is, in hexadecimal:
0x0.ffffffffffffffff
这个值和下一个更高的可表示数字 (1.0) 之间的正好中间的数字是:
The number exactly halfway between this value and the next higher representable number (1.0) is:
0x0.ffffffffffffffff8
你很长的常数 0.9999999999999999999728949456878623891498136799780 等于:
Your very long constant 0.9999999999999999999728949456878623891498136799780 is equal to:
0x0.ffffffffffffffff7fffffffffffffffffffffffa1eb2f0b64cf31c113a8ec...
如果舍入到最接近的值,显然应该向下舍入,但您似乎已经达到编译器使用的浮点表示的某些限制,或者舍入错误.
which should obviously be rounded down if rounding to nearest, but you appear to have reached some limit of the floating point representation your compiler is using, or a rounding bug.
相关文章