質問
今朝、ちょっとした驚きの瞬間がありました。このなんとも言えないことを次のように要約できます。
float x = 0.2f;
float y = 0.1f;
float z = x + y;
assert(z == x + y); //This assert is triggered! (Atleast with visual studio 2008)
その理由は、次のような表現にあるようです。 x + y
double にプロモートされ、切り捨てられたバージョンと比較されます。 z
. 。(もし私が変われば z
に double
アサートはトリガーされません)。
精度上の理由から、結果を単精度に変換する前に、すべての浮動小数点演算を倍精度で実行することが合理的であることがわかります。標準規格に次の段落を見つけました (これはすでに知っていたと思いますが、この文脈ではそうではありません)。
4.6.1.「型の右辺値 float
型の右辺値に変換できます。 double
. 。価値は変わらないよ」
私の質問は、 x + y
double に昇格することが保証されていますか、それともコンパイラーの裁量に任されていますか?
アップデート: 多くの人が使用すべきではないと主張しているため、 ==
浮動小数点の場合、私が扱っている特定のケースでは、正確な比較が正当であることを述べたかっただけです。
浮動小数点の比較 は 難しいですね、ここが興味深いです リンク これまで言及されていなかったと思うテーマについて。
解決
一般的には想定できません ==
浮動小数点型では期待どおりに機能します。丸められた値を比較するか、次のような構成を使用します abs(a-b) < tolerance
その代わり。
プロモーションは完全にコンパイラーの裁量によって行われます (ターゲットのハードウェア、最適化レベルなどによって異なります)。
この特定のケースで何が起こっているかというと、ほぼ確実に値がメモリよりも高い精度で FPU レジスタに格納されています。一般に、最新の FPU ハードウェアは、プログラマが要求した精度に関係なく、コンパイラがコードを生成することで、内部的には 2 倍以上の精度で動作します。値がメモリに保存されるときに適切な変換を行うため。最適化されていないビルドでは、次の結果が発生します。 x+y
比較が行われた時点ではまだレジスタ内にありますが、 z
メモリに保存されてフェッチバックされるため、float 精度に切り捨てられます。
他のヒント
の 次期標準 C++0x の作業草案 セクション 5 のポイント 11 に記載されています
浮動小数点オペランドの値と浮動小数点式の結果は、その型で必要な精度と範囲よりも高い精度と範囲で表現される場合があります。それによってタイプは変更されません
したがって、コンパイラの裁量によります。
gcc 4.3.2 を使用すると、アサーションは次のようになります。 ない トリガーされ、実際に返される右辺値は x + y
です float
, ではなく、 double
.
したがって、それはコンパイラ次第です。このため、2 つの浮動小数点値が完全に等しいことに依存するのは決して賢明ではありません。
C++ FAQ Lite には、このトピックについてさらに詳しい説明があります。
浮動小数点数からバイナリへの変換では正確な精度が得られないため、これが問題になります。
そしてその中で sizeof(float)
バイト数は浮動小数点数の正確な値を収容できず、算術演算が近似を引き起こす可能性があるため、等価性が失われます。
以下を参照してください。
float x = 0.25f; //both fits within 4 bytes with precision
float y = 0.50f;
float z = x + y;
assert(z == x + y); // it would work fine and no assert
それはコンパイラの裁量によると思いますが、それがあなたの考えであれば、いつでもキャストを使用して強制することができますか?
float を直接比較しないもう 1 つの理由。
if (fabs(result - expectedResult) < 0.00001)