为什么在浮点数的末尾加上0会改变它的舍入方式(可能的GCC bug)
Why does adding 0 to the end of float literal change how it rounds (possible GCC bug)?
我发现在我的x86虚拟机(32位)上有以下程序:
#include <stdio.h>
void foo (long double x) {
int y = x;
printf("(int)%Lf = %dn", x, y);
}
int main () {
foo(.9999999999999999999728949456878623891498136799780L);
foo(.999999999999999999972894945687862389149813679978L);
return 0;
}
产生以下输出:
(int)1.000000 = 1
(int)1.000000 = 0
Ideone也产生此行为。
编译器做了什么来允许这种情况发生?
我发现了这个常数,因为我正在跟踪为什么以下程序没有像我预期的那样产生0
(使用19个9
s产生我预期的0
):
int main () {
long double x = .99999999999999999999L; /* 20 9's */
int y = x;
printf("%dn", y);
return 0;
}
当我试图计算结果从预期值转换到非预期值时,我得到了这个问题所涉及的常数
您的问题是平台上的long double
没有足够的精度来存储确切的值0.999999999999999999999999。这意味着它的值必须被转换成一个可表示的值(这种转换发生在程序的翻译过程中,而不是在运行时)。
这个转换可以生成最近的可表示值,或者下一个更大或更小的可表示值。选择是由实现定义的,因此您的实现应该记录它正在使用的是哪个。似乎你的实现使用了x87风格的80位long double
,并且四舍五入到最接近的值,导致x
中存储的值为1.0。
对于long double
的假定格式(64尾数位),小于1.0的可表示的最大数字是,十六进制:
0x0.ffffffffffffffff
这个值和下一个更高的可表示数(1.0)之间的数字是:
0x0.ffffffffffffffff8
你的长常数0.9999999999999999999728949456878623891498136799780等于:
0x0.ffffffffffffffff7fffffffffffffffffffffffa1eb2f0b64cf31c113a8ec...
如果四舍五入到最接近,显然应该向下舍入,但您似乎已经达到了编译器使用的浮点表示的某些限制,或者是舍入错误。
编译器使用二进制数。大多数编译器都做同样的事情。
根据wolframalpha,
的二进制表示 0.99999999999999999999
看起来像这样:
0.11111111111111111111111111111111111111111111111111111111111111111101000011000110101111011110011011011011011110111011100101000101010111011100001011010001001110001101011001010000110000101001111011111001111110000101010111111110100110000010001001101011001101010110110010010101101111101001110001100111101100000000100110110001100110000011000100100011000011110100001000000100001000101000111011010111111101011010010000010110011111110100100110001011001110100011100001111101011110101001000000111110010000101101001001010110010011001110111111100111101111100000111010001101101011000100110001010010001000100010110000101110100101010101001010100010001001100111111111001001101100000000010010001011110100101011101001001101001111001001000101011101001100111101110111111001101110100111000001111101101101101101110100100111101000000000111101101101001000111101100010101110011101110001110010110110111101000011110110100011000110101100011111111110111000010010001111000000000101100101000100101110100001001101000010110101000100011100000110010001110101...
这是932位,这仍然不足以精确地表示你的数字(见末尾的点)。
这意味着只要你的底层平台使用以2为基数来存储数字,你就不能准确地存储0.99999999999999999999
。
因为number不能精确存储,所以它将被四舍五入。如果是20个9,它将被四舍五入,如果是19个9,它将被四舍五入
为了避免这个问题,你需要使用某种第三方数学/bignum库来代替双精度数,该库内部使用十进制基数(即每字节两个十进制数字或其他东西)存储数字,或者使用分数(比率)而不是浮点数。那就解决你的问题了。
双精度值,当没有足够的精度来表示一个值时,向上或向下舍入到最接近的值。在你的实现中,它是四舍五入到1。
这里涉及到两个转换。首先,在某种程度上也是最重要的,是将字面量.99999999999999999999L
转换为长双精度型。正如其他人所说,这种转换将舍入到最接近的可表示值,这似乎是1.0L
。第二次转换是从第一次转换产生的长双精度值到整数值。该转换将四舍五入到0,这就是为什么快速检查表明y
的值应该为0的原因。但是因为第一个转换产生的是1,而不是一个略小于1的值,所以这个转换也产生1。
- C++将浮点指针值舍入为小数位数
- Boost::posix_time::ptime舍入到给定的分钟数
- 浮点定向舍入和优化
- 为什么输出精度没有正确舍入?
- 使用设置精度时如何阻止数字向上舍入?
- 如何在使用 MPFR 时在提升多精度中设置舍入模式
- OpenCV 细分一致的 Rect2f 到 Rect2i 舍入
- C++ 中的舍入函数
- 舍入 QDecDouble 值,精度最多为两个字符
- 将大 int 转换为浮点数,而不舍入 c++
- 舍入错误检测
- 根据浮点符号对浮点进行舍入的最简单方法是什么
- 将整数舍入到另一个整数的最接近的倍数
- Sizeof 舍入到对齐方式,但编译器仍将对象放在剩余的字节中
- 避免将 Int 转换为双重类型转换舍入
- 浮点数学运算后舍入不一致
- 浮动到绳子而无需舍入
- C++长双精度格式而不舍入
- 如何舍入到下一个 32 位对齐方式
- 为什么在浮点数的末尾加上0会改变它的舍入方式(可能的GCC bug)