سؤال

There is a piece of code confuse me, which runs in windows! Here is the code:

#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, %x\n", num[i], af_f, point_float2uint(af_f), af_i, point_float2uint(af_i));
    }
}

Here is the output, compiled by 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

The question is: why I use the "divide_1000", get the different result in windows? This is not what I want! And I find that not all the integer result in different, but some just like the code above.

Here is the the output, comipled by gcc4.4.5 in debian:

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

I get the same result in useing different function "divide_1000". That's what I want.

هل كانت مفيدة؟

المحلول

There are quite a few code generation settings involved here that affect the outcome. The difference that you report is observable in non-optimized code under default floating point model (i.e. "precise" model) when using the "classic" FPU instructions for floating-point computations.

The compiler translates the first call literally: the original integer value is first converted to float - 4-byte floating-point value - stored in memory (as function argument). This conversion rounds the value to +6.7975504e+7, which is already not precise. Later that float value is read form memory inside the first function and used for further computations.

The second call passes an int value to the function, which is directly loaded into high-precision FPU register and used for further computations. Even though you specified an explicit conversion from int to float inside the second function, the compiler decided to ignore your request. This value is never literally converted to float, meaning that the aforementioned loss of precision never occurs.

That is what is causing the difference you observed.

If you rewrite your second function as

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

i.e. add an additional step that saves the float value to a named location in memory, the compiler will perform that step in non-optimized code. This will cause the results to become identical.

Again, the above applies to the code compiled without optimizations, when the compiler normally attempts to translate all statements very closely (but not always exactly). In optimized code the compiler eliminates the "unnecessary" intermediate conversions to float and all "unnecessary" intermediate memory stores in both cases, producing identical results.

You might also want to experiment with other floating-point models (i.e. "strict" and "fast") to see how it affects the results. These floating-point models exist specifically to deal with issues like the one you observed.

If you change code generation settings of the compiler and make it use SSE instructions for floating-point arithmetic, the results might also change (in my experiment the difference disappears when SSE2 instruction set is used instead of FPU instructions).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top