繁体   English   中英

为什么在转换为int之前在变量中存储double表达式会导致与直接转换结果不同的结果?

[英]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 

为什么ad有不同的值,即使它们都是b100的乘积?

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.0000long 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM