1

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.

4

1 に答える 1

3

ここには、結果に影響を与える非常に多くのコード生成設定が含まれています。あなたが報告する違いは、浮動小数点計算に「古典的な」FPU命令を使用する場合、デフォルトの浮動小数点モデル(つまり「正確な」モデル)の下で最適化されていないコードで観察できます。

コンパイラは最初の呼び出しを文字どおりに変換します。元の整数値は最初にfloat- 4 バイトの浮動小数点値 - に変換され、メモリに (関数の引数として) 格納されます。この変換により、値が に丸められますが+6.7975504e+7、これはまだ正確ではありません。その後、その float値は最初の関数内のメモリから読み取られ、さらなる計算に使用されます。

2 番目の呼び出しはint値を関数に渡します。値は高精度 FPU レジスタに直接ロードされ、その後の計算に使用されます。intからへの明示的な変換をfloat2 番目の関数内で指定したにもかかわらず、コンパイラは要求を無視することを決定しました。この値は文字どおり に変換されることはありませんfloat。つまり、前述の精度の損失は発生しません。

それが、あなたが観察した違いの原因です。

2番目の関数を次のように書き換えると

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

つまり、メモリ内の名前付きの場所に値を保存する追加のステップを追加するfloatと、コンパイラは最適化されていないコードでそのステップを実行します。これにより、結果が同一になります。

繰り返しになりますが、上記は最適化なしでコンパイルされたコードに適用されます。これは、コンパイラが通常、すべてのステートメントを非常に厳密に変換しようとする場合です (常に正確であるとは限りません)。最適化されたコードでは、コンパイラfloatは両方のケースで「不要な」中間メモリ ストアへの「不要な」中間変換とすべての「不要な」中間メモリ ストアを排除し、同じ結果を生成します。

また、他の浮動小数点モデル (つまり、"strict" および "fast") を試して、それが結果にどのように影響するかを確認することもできます。これらの浮動小数点モデルは、あなたが観察したような問題に対処するために特別に存在します。

コンパイラのコード生成設定を変更して、浮動小数点演算に SSE 命令を使用するようにすると、結果も変わる可能性があります (私の実験では、FPU 命令の代わりに SSE2 命令セットを使用すると、違いはなくなります)。

于 2013-07-03T07:45:38.853 に答える