[英]Why storing a double expression in a variable before a cast to int can lead to different results than casting it directly?
我编写这个简短的程序来测试从double到int的转换:
int main() {
int a;
int d;
double b = 0.41;
/* Cast from variable. */
double c = b * 100.0;
a = (int)(c);
/* Cast expression directly. */
d = (int)(b * 100.0);
printf("c = %f \n", c);
printf("a = %d \n", a);
printf("d = %d \n", d);
return 0;
}
输出:
c = 41.000000
a = 41
d = 40
为什么a
和d
有不同的值,即使它们都是b
和100
的乘积?
C标准允许C实现计算浮点运算,其精度高于标称类型。 例如,对于IEEE-754 64位格式,当源代码中的类型为double
,可以使用Intel 80位浮点格式。 在这种情况下,可以通过假设C实现尽可能使用long double
(80 bit)并在C标准需要时转换为double
来完全解释行为。
我猜想在这种情况下发生的事情是:
double b = 0.41;
, 0.41
被转换为double
并存储在b
。 转换结果的值略小于.41。 double c = b * 100.0000;
, b * 100.0000
以long double
评估。 这会产生略小于41的值。 c
。 C标准要求在此时将其转换为double
。 因为该值非常接近41,所以转换恰好产生41.因此c
为41。 a = (int)(c);
像往常一样产生41。 d = (int)(b * 100.000);
,我们有与以前相同的乘法。 该值与之前相同,略小于41.但是,此值未分配或初始化为double
,因此不会转换为double
。 相反,它被转换为int
。 由于该值略小于41,因此转换产生40。 编译器可以推断c
必须用0.41 * 100.0
初始化,并且比d
的计算更好。
问题的关键是0.41
在IEEE 754 64位二进制浮点中不能完全表示。 实际值(仅显示相关部分的精度足够)为0.409999999999999975575...
,而100
可以精确表示。 将这些相乘可以得到40.9999999999999975575...
,这再次不能代表。
在可能的情况下,舍入模式朝向最接近,零或负无穷大,这应该四舍五入为
40.9999999999999964...
当转换为int时,将舍入为
40
。
然而,允许编译器以更高的精度进行计算,并且特别地可以用直接存储计算值来替换c
的赋值中的乘法。
编辑:我错误计算了小于41的最大可表示数字,正确值约为40.99999999999999289...
正如Eric Postpischil和Daniel Fischer都正确指出的那样,即使计算为double的值也应该四舍五入为41
除非舍入模式为零或负无穷大。 你知道舍入模式是什么吗? 它有所不同,因为此代码示例显示:
#include <stdio.h>
#include <fenv.h>
#pragma STDC FENV_ACCESS ON
int main(void)
{
int roundMode = fegetround( );
volatile double d1;
volatile double d2;
volatile double result;
volatile int rounded;
fesetround(FE_TONEAREST);
d1 = 0.41;
d2 = 100;
result = d1 * d2;
rounded = result;
printf("nearest rounded=%i\n", rounded);
fesetround(FE_TOWARDZERO);
d1 = 0.41;
d2 = 100;
result = d1 * d2;
rounded = result;
printf("zero rounded=%i\n", rounded);
fesetround(roundMode);
return 0;
}
输出:
nearest rounded=41
zero rounded=40
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.