9

だから私はこのような関数を持っています:

float function(){
    float x = SomeValue;
    return x / SomeOtherValue;
}

ある時点で、この関数はオーバーフローし、非常に大きな負の値を返します。これが発生している場所を正確に追跡するために、関数が次のようになるようにcoutステートメントを追加しました。

float function(){
    float x = SomeValue;
    cout << x;
    return x / SomeOtherValue;
}

そしてそれはうまくいきました!もちろん、ダブルを使って問題を完全に解決しました。しかし、私がそれを吐いたときに、なぜ関数が正しく機能したのかについて興味があります。これは典型的なものですか、それとも私が見逃しているバグがどこかにある可能性がありますか?

(助けになる場合は、floatに格納される値は単なる整数値であり、特に大きな値ではありません。キャストを避けるために、floatに入れるだけです。)

4

5 に答える 5

18

浮動小数点の素晴らしい世界へようこそ。得られる答えは、コードをコンパイルした浮動小数点モデルによって異なる可能性があります。

これは、IEEE仕様とコードが実行されているハードウェアの違いが原因で発生します。CPUには、32ビット浮動小数点値を保持するために使用される80ビット浮動小数点レジスタが含まれている可能性があります。これは、値がメモリアドレスに強制されるとき(レジスタの「ホーミング」とも呼ばれます)よりも、値がレジスタに留まっているときの方がはるかに精度が高いことを意味します。

値をcoutに渡すと、コンパイラは浮動小数点をメモリに書き込む必要があり、その結果、精度が失われ、WRTオーバーフローのケースで興味深い動作が発生します。

VC++浮動小数点スイッチに関するMSDNドキュメントを参照してください。/ fp:strictを使用してコンパイルし、何が起こるかを確認してみてください。

于 2008-09-08T03:43:10.770 に答える
3

coutに値を出力しても、パラメータの値はまったく変更されません。

ただし、同様の動作を確認しました。デバッグステートメントを追加すると、値が変更されます。そのような場合、そしておそらくこれも私の推測では、追加のステートメントがコンパイラのオプティマイザの動作を変えていたので、関数に対して異なるコードを生成しました。

coutステートメントを追加すると、xの値が直接使用されます。これがないと、オプティマイザーは変数を削除する可能性があるため、計算の順序を変更して、答えを変更します。

于 2008-09-08T03:20:31.463 に答える
2

余談ですが、以下を使用して不変変数を宣言することをお勧めしますconst

float function(){
    const float x = SomeValue;
    cout << x;
    return x / SomeOtherValue;
}

とりわけ、これにより、非const参照を介して変数を変更する可能性のある関数に変数を意図せずに渡すことがなくなります。

于 2008-09-08T16:16:46.653 に答える
1

cout は変数への参照を引き起こします。これにより、多くの場合、コンパイラが強制的に変数をスタックにスピルさせます。

これは float であるため、その値が double または long double 表現から切り捨てられる可能性があります。

x へのポインターまたは参照を受け取る関数 (インライン化されていない) を呼び出すと、最終的に同じ動作が発生するはずですが、後でコンパイラーが賢くなり、インライン化することを学習した場合、同様に失敗します :)

于 2010-08-31T10:07:05.090 に答える
0

coutが変数に影響を与えるとは思わないので、問題は別の場所にある必要があります。

于 2008-09-08T03:21:04.817 に答える