17

我编写了这个简短的程序来测试从 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具有不同的值,即使它们都是 和 的b乘积100

4

3 回答 3

18

C 标准允许 C 实现以比标称类型更高的精度计算浮点运算。例如,当源代码中的类型为 时,可以使用 Intel 80 位浮点格式double,对于 IEEE-754 64 位格式。long double在这种情况下,可以通过假设 C 实现尽可能使用(80 位)并转换到doubleC 标准需要它的时候来完全解释该行为。

我猜想在这种情况下会发生什么:

  • double b = 0.41;,0.41中转换double并存储在b. 转换产生的值略小于 0.41。
  • double c = b * 100.0000;中,b * 100.0000在 中进行评估long double。这会产生一个略小于 41 的值。
  • 该表达式用于初始化c. C 标准要求此时将其转换为double。因为该值非常接近 41,所以转换正好产生 41。41 也是如此c
  • a = (int)(c);正常产生 41。
  • d = (int)(b * 100.000);中,我们有与以前相同的乘法。该值与以前相同,略小于 41。但是,该值未分配给或用于初始化 a double,因此不会发生转换double。相反,它被转换为int. 由于该值略小于 41,因此转换产生 40。
于 2013-06-20T18:32:17.070 回答
1

编译器可以推断出c必须用 初始化,0.41 * 100.0并且比计算d.

于 2013-06-20T18:16:58.553 回答
1

问题的症结在于0.41IEEE 754 64 位二进制浮点不能完全表示。实际值(只有足够的精度来显示相关部分)是0.409999999999999975575..., 而100可以精确表示。将这些相乘应该得到40.9999999999999975575...,这又不太具有代表性。在舍入模式接近最接近、零或负无穷大的可能情况下,应将其舍入为40.9999999999999964...。当转换为 int 时,将四舍五入为40.

然而,允许编译器以更高的精度进行计算,特别是可以用c计算值的直接存储来代替赋值中的乘法。


编辑:我误算了小于 41 的最大可表示数,正确的值大约是40.99999999999999289.... 正如 Eric Postpischil 和 Daniel Fischer 都正确指出的那样,即使是作为双精度计算的值也应该四舍五入,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
于 2013-06-20T18:58:33.490 回答