为什么在浮点文字的末尾添加 0 会改变它的舍入方式(可能是 GCC 错误)? [英] Why does adding 0 to the end of float literal change how it rounds (possible GCC bug)?

查看:18
本文介绍了为什么在浮点文字的末尾添加 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 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.

这篇关于为什么在浮点文字的末尾添加 0 会改变它的舍入方式(可能是 GCC 错误)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆