为什么在浮点文字的末尾添加 0 会改变它的舍入方式(可能是 GCC 错误)?

2022-01-09 00:00:00 gcc floating-point c c++

我在我的 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 9s 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.

相关文章