9

この例の C++ コード スニペットを考えると、次のようになります。

void floatSurprise()
{
    // these come from some sort of calculation
    int a = 18680, b = 3323524, c = 121;
    float m = float(a) / c;

    // variant 1: calculate result from single expression
    float r1 = b - (2.0f * m * a) + (m * m * c);
    cout << "r1 = " << r1 << endl;

    // variant 2: break up the expression into intermediate parts, 
    /// then calculate 
    float
        r2_p1 = 2.0f * m * a,
        r2_p2 = m * m * c,
        r2 = b - r2_p1 + r2_p2;

    cout << "r2 = " << r2 << endl;
}

出力は次のとおりです。

dev1 = 439703
dev2 = 439702

デバッガーで表示すると、値は実際にはそれぞれ 439702.50 と 439702.25 であり、それ自体が興味深いものです。iostream がデフォルトで小数部なしで float を出力する理由がわかりません。編集:この理由は、cout のデフォルトの精度設定が低すぎたためです。この大きさの数値の小数点を表示するには、少なくとも cout << setprecision(7) が必要でした。

しかし、なぜ異なる結果が得られたのかということにさらに興味があります。丸めと、int と必要な float 出力タイプとの微妙な相互作用に関係していると思いますが、指を置くことはできません。正しい値はどれですか?

このような単純なコードで簡単に自分の足を撃つことができることに驚きました。どんな洞察も大歓迎です!コンパイラは VC++2010 でした。

EDIT2:スプレッドシートを使用して中間変数の「正しい」値を生成するためにさらに調査を行い、(トレースを介して)実際にそれらがトリミングされており、最終的な結果の精度の低下に寄与していることがわかりました。また、単一の式に問題があることもわかりました。実際には、代わりに二乗を計算するための便利な関数を使用したためですm * m

template<typename T> inline T sqr(const T &arg) { return arg*arg; }

私がうまく尋ねたとしても、コンパイラは明らかにこれをインライン化せず、値を個別に計算し、値を式に返す前に結果をトリミングし、結果をさらに歪めました。ああ。

4

1 に答える 1

11

You should read my long, long answer about why the same thing happens in C#:

(.1f+.2f==.3f) != (.1f+.2f).Equals(.3f) Why?

Summing up: first of all, you only get about seven decimal places of accuracy with float. The correct answer were you do to it with exact arithmetic throughout the entire calculation is about 439702.51239669... so you are getting darn close to the correct answer considering the limitations of a float, in either case.

But that doesn't explain why you are getting different results with what looks like exactly the same calculations. The answer is: the compiler is permitted wide lattitude to make your math more accurate, and apparently you have hit upon two cases where the optimizer takes what is logically the same expression and does not optimize them down to the same code.

Anyway, read my answer regarding C# carefully; everything in there applies to C++ just as well.

于 2013-03-19T01:49:54.613 に答える