7

2つのfloat値を比較して、GCCの最適化で見られる問題を説明する簡単なコードがあります。再現可能な状況で、生成される出力が異なる理由を誰かが理解できるようになることを願っています。

まず、仮数でごくわずかにずれている可能性があるため、float値を==と比較するのは悪いことですが、私の例ではそうではありません。私が抱えている問題は、2つの要因に基づいて出力が変化することです。1)渡した最適化フラグ、および2)std::cout行のコメントを解除した場合。

GCCが生成するコードが-O2で異なる動作をするのはなぜですか?印刷のコメントを外すと、-O2でコンパイルされたコードが機能するのはなぜですか?

これが私がテストしているコードです:

#include <iostream>

const float ft_to_m          =  (float)0.3048; 
const float m_to_ft          =  (float)3.28083989501;


float FeetToKilometers( float & Feet ) {
  float Kilometers;
  Kilometers = (ft_to_m * Feet) / 1000.;
  return Kilometers;
}

int main(void)
{
    float feet = 20000.;
    float old_val = 0;
    float new_val = FeetToKilometers(feet );
    float diff_val = 0;

    int *old_int = reinterpret_cast<int*>(&old_val);
    int *new_int = reinterpret_cast<int*>(&new_val);

    for (int i=0; i<2; i++)
    {

    new_val = FeetToKilometers(feet );
    diff_val = old_val-new_val;

    //std::cout << "Random COUT that makes this work" << std::endl;

        if(old_val==new_val)
    {
             std::cout << "old_val==new_val" << std::endl;
         std::cout << std::hex << *old_int << "," << std::hex << *new_int << std::endl;
             std::cout << "diff_val = " << diff_val <<std::endl;
    }
        else
        {
            std::cout << "old_val!=new_val" <<std::endl;
        std::cout << std::hex << *old_int << "," << std::hex << *new_int << std::endl;
            std::cout << "diff_val = " << diff_val <<std::endl;
            old_val=FeetToKilometers(feet);
        }
    }

    return 0;
}

linux / cygwinで-O0、-O1、および-O3(g ++ -O test.cpp)を使用してコンパイルすると、次の出力が得られます。


$ ./a.exe
old_val!= new_val
0,40c3126f
diff_val = -6.096
old_val == new_val
40c3126f、40c3126f
diff_val = 0


その出力は正しく、floatのビット(new_valとold_val)が同一であることがわかります。-O2フラグ(g ++ -O2 test.cpp)を使用してコンパイルすると、次のようになります。


$ ./a.exe
old_val!= new_val
0,40c3126f
diff_val = -6.096
old_val!= new_val
40c3126f、40c3126f
diff_val = 1.19209e-07


この出力は間違っていると思います。2つの値はビット単位で同じですが、それらを減算して==チェックすると、それらが異なることを示します。次に、std :: cout行のコメントを解除し、-O2フラグ(g ++ -O2 test.cpp)を使用して再構築すると、次のようになります。


$ ./a.exe
この作業を行うランダム
COUTold_val!= new_val
0,40c3126f
diff_val=-6.096
この作業を行うランダム
COUTold_val== new_val
40c3126f、40c3126f
diff_val = 1.19209e-07


これは、減算がまだわずかな違いを示している場合でも、そのold_val==new_valでは正しいです。

このコードは、フィートが20000ではなく2000の場合、-O2でも機能します。

コンパイルされたコードがこのように動作する理由を誰かが説明できますか?2ビットの同一のfloat値を==と比較できない理由を知りたいです。

gccバージョン3.4.4

4

1 に答える 1

12

最適化レベルと周囲のコードは、diff_val計算で使用される値がメモリからフェッチされるか、レジスタからフェッチされるかに影響する場合があります。プロセッサは、あるケースでは 80 ビットの内部浮動小数点レジスタを使用し、別のケースではメモリからの 32 ビット浮動小数点値を使用している可能性があり、予期しない結果をもたらします。

==浮動小数点の比較に使用することを避けるもう 1 つの理由です。

于 2010-09-21T23:34:10.313 に答える