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