在mingw32中的Double到int的隐式转换

19

我无法解释以下程序的行为(使用mingw 32位上的gcc编译)。我知道将double隐式转换为int时可能会出现精度损失,但我期望两种情况给出相同的输出,因为它们执行的是完全相同的操作。为什么这两个输出不同?

#include <stdio.h>
#include <math.h>

int main()
{
    int table[3] = {2, 3, 4};
    int i, N;

    N = 0;
    N += table[0] * pow(100, 0);
    N += table[1] * pow(100, 1);
    N += table[2] * pow(100, 2);
    printf("%d\n", N);

    N = 0;
    for(i = 0; i < 3; i++)
        N += table[i] * pow(100, i);
    printf("%d\n", N);

    return 0;
}

//output: 
40302
40300
1个回答

14

使用pow(100, 0)pow(100, 1)pow(100, 2),编译器将函数调用替换为常量(1, 100, 10000),但是对于pow(100, i),由于传递的参数是变量i,编译器必须在运行时实际调用该函数,导致两个pow的结果形式为0.9999999999.999999,而不是1100(或其中任意两个)。在乘法后截断为整数时,“丢失”了两个单位。

这是另一个例子,说明从double转换为int只是纯粹的邪恶: 在程序中很难发现微妙的错误(不是编译器错误)。

顺便说一下,我很惊讶编译器使用O2没有展开循环、传播常量并达到相同的优化(用常量结果替换函数调用)。

顺便说一下2,我很惊讶编译器没有仅用两个printf调用替换你的所有代码。


我认为值得一提的是,C标准实际上并没有规定“pow()”的精度,甚至不要求“pow()”在pow(0)和pow(i)(其中i=0)时给出相同的结果。这对于非常小的嵌入式系统进行优化是有意义的,但对我来说确实令人惊讶。 - Étienne
有趣的是,当循环只有两次迭代时,编译器会将其优化掉。 - vgru
@Groo,你的链接指向了一个不同的编译器(gcc 9.2 x86-64),所以结果不同并不奇怪。即使在循环中进行三次迭代,也无法使用gcc 9.2复现我的问题,我的问题是特定于mingw-32位GCC的。 - Étienne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接