奇怪的,在窗口中传递函数的浮点型参数

Strange, float type parameter passing in function in windows

本文关键字:传递函数 浮点型 参数 窗口      更新时间:2023-10-16

有一段代码让我很困惑,它在windows中运行!这是代码:

#define point_float2uint(x) *((unsigned int *)&x)

float divide_1000(float y)
{
    float v = y / 1000.0f;
    return v;
}
float divide_1000(int y)
{
    float v = float(y) / 1000.0f;
    return v;
}

void float_test(void)
{
    int num[5] = {67975500, 67251500, 67540620, 69435500, 70171500};
    for (int i = 0; i < 5; ++i)
    {
        int a = num[i];
        float af_f = divide_1000(float(a));
        float af_i = divide_1000((a));
        printf("src num:%d,  af_f:%f, %x, af_i:%f, %xn", num[i], af_f, point_float2uint(af_f), af_i, point_float2uint(af_i));
    }
}

以下是输出,由vs2005:编译

src num:67975500,  af_f:67975.507813, 4784c3c1, af_i:67975.500000, 4784c3c0
src num:67251500,  af_f:67251.507813, 478359c1, af_i:67251.500000, 478359c0
src num:67540620,  af_f:67540.625000, 4783ea50, af_i:67540.617188, 4783ea4f
src num:69435500,  af_f:69435.507813, 47879dc1, af_i:69435.500000, 47879dc0
src num:70171500,  af_f:70171.507813, 47890dc1, af_i:70171.500000, 47890dc0

问题是:为什么我使用"divide_1000",在窗口中得到不同的结果?这不是我想要的!我发现并不是所有的整数都会产生不同的结果,但有些结果就像上面的代码一样。

以下是输出,由debian:中的gcc4.4.5表示

src num:67975500,  af_f:67975.507812, 4784c3c1, af_i:67975.507812, 4784c3c1
src num:67251500,  af_f:67251.507812, 478359c1, af_i:67251.507812, 478359c1
src num:67540620,  af_f:67540.625000, 4783ea50, af_i:67540.625000, 4783ea50
src num:69435500,  af_f:69435.507812, 47879dc1, af_i:69435.507812, 47879dc1
src num:70171500,  af_f:70171.507812, 47890dc1, af_i:70171.507812, 47890dc1

在使用不同的函数"divide_1000"时,我得到了相同的结果。这就是我想要的。

这里涉及到许多影响结果的代码生成设置。当使用"经典"FPU指令进行浮点计算时,在默认浮点模型(即"精确"模型)下的非优化代码中可以观察到您报告的差异。

编译器从字面上翻译第一个调用:原始整数值首先转换为float——4字节浮点值——存储在内存中(作为函数参数)。此转换将值四舍五入为+6.7975504e+7,这已经不精确了。稍后,float值从第一个函数内的存储器中读取,并用于进一步的计算。

第二个调用将int值传递给函数,该函数直接加载到高精度FPU寄存器中,用于进一步计算。尽管您在第二个函数中指定了从intfloat的显式转换,编译器还是决定忽略您的请求。该值从未从字面上转换为float,这意味着上述精度损失从未发生。

这就是你观察到的差异的原因。

如果您将第二个函数重写为

float divide_1000(int y)
{
    float fy = y;
    float v = fy / 1000.0f;
    return v;
}

即添加一个将float值保存到内存中命名位置的附加步骤,编译器将在未优化的代码中执行该步骤。这将导致结果变得相同。

同样,当编译器通常试图非常紧密地翻译所有语句(但并不总是准确地)时,上面的内容适用于在没有优化的情况下编译的代码。在优化的代码中,编译器消除了对float的"不必要"的中间转换,以及在这两种情况下所有"不必要的"中间内存存储,从而产生相同的结果。

您可能还想对其他浮点模型(即"严格"answers"快速")进行实验,看看它如何影响结果。这些浮点模型专门用于处理您所观察到的问题。

如果您更改编译器的代码生成设置,并使其使用SSE指令进行浮点运算,结果也可能会发生变化(在我的实验中,当使用SSE2指令集而不是FPU指令时,差异会消失)。