私はVisualStudio6を使用しており、cで記述された古いコードがいくつかあります。コードが次のようになる問題を見つけました。
int x = 3;
float y = 3.0;
if(x == y){
do some crazy stuff
}
これは有効な比較ですか?実行時にフロートの割り当てが3.0000001であり、これが失敗する可能性はありますか?
私はVisualStudio6を使用しており、cで記述された古いコードがいくつかあります。コードが次のようになる問題を見つけました。
int x = 3;
float y = 3.0;
if(x == y){
do some crazy stuff
}
これは有効な比較ですか?実行時にフロートの割り当てが3.0000001であり、これが失敗する可能性はありますか?
これは一般的に(つまり、常に)悪い考えです。ご想像のとおり、3から3.0000001への比較は実際に失敗します。
int-float比較が本当に必要な場合、ほとんどの人が行うことは、許容範囲のしきい値を選択し、次のようにそれを実行することです。
int x = 3;
float y = 3.0;
// some code here
float difference = (float) x - y;
float tolerableDifference = 0.001;
if ((-tolerableDifference <= difference) && (difference <= tolerableDifference)) {
// more code
}
ここで少しトレンドに逆行します。比較が有効かどうかに関する最初の質問については、答えはイエスです。それは完全に有効です。浮動小数点値が正確に 3 に等しいかどうかを知りたい場合は、整数との比較で問題ありません。整数は、比較のために暗黙的に浮動小数点値に変換されます。実際、次のコードは (少なくとも私が使用したコンパイラでは) 同一のアセンブリ命令を生成しました。
if ( 3 == f )
printf( "equal\n" );
と
if ( 3.0 == f )
printf( "equal\n" );
したがって、それはロジックと意図された目標が何であるかによって異なります。構文に本質的な問題はありません。
他の誰もまだ引用しておらず、しばらくリンクしていなかったので、浮動小数点表現と算術演算の恐ろしいエッジに関する古典的な論文を次に示します。すべてのコンピューター科学者が浮動小数点について知っておくべきこと.
この論文は数学者ではない人にとっては読みにくいものですが、重要なポイントは、それらを裏付ける大量の数学の帯の間に十分に記載されています.
この議論では、ここでの他の回答によって指摘された点はすべて有効です。浮動小数点演算は不正確であるため、完全に等しいかどうかを比較することは、一般的にはお勧めできません。したがって、イプシロンはあなたの友達です。
正確な比較ルールの 1 つの例外は、正確にゼロのテストです。答えはゼロ以外の値に対して明確に定義されているため、除算または対数の前に正確にゼロをテストすることは完全に合法であり、多くの場合賢明です。もちろん、IEEE ルールと NaN が存在する場合は、それをスライドさせて、後で NaN または Inf をテストすることができます。
さて、フロートを平等と比較することは新人の間違いだと聞いても驚くことはないでしょう。
問題は、整数値よりも小さい多くの増分が実際にはIEEE浮動小数点で正確に表現できないことです。したがって、3.0の値まで(たとえば0.1刻みで)「インデックス付け」を試みてフロートに到達した場合、等式の比較が真になることはあり得ない可能性があります。
タイプ強度の観点からも悪い考えです。floatをintに変換するか、intが「十分に近い」(たとえば、<3.1および> 2.9など)かどうかを確認するか、さらに、そのfloatをカウンターのようなものに対して2つの役割を果たそうとしている場合は、 、アイデア全体を避けてください。
あなたの特定の例では、「いくつかのクレイジーなことをする」が実行されます。3.0は実行時に3.0000001にはなりません。
他の答えはより一般的な場合ですが、ハードコードされたイプシロンでさえ、世界で最高のアイデアではありません。関係する実際の数値に基づく動的イプシロンは、数値が正で負であるほど、ハードコードされたイプシロンが関連する可能性が低くなるため、はるかに優れています。
それが怖いです。(他に何が見つかるのかしら。)
x は float に昇格されますが、それは役に立ちません。float の表現方法が原因で、 == を使用してそれらを比較することは信頼できません。
代わりに、次のようなことを提案するかもしれません (絶対エラー/差異をチェックする):
#define EPSILON 0.0001
if (fabs((float)x - y) < EPSILON) { /* Do stuff. */ }
これは一般的なアプローチであり、x と y の値が「適切」である場合、目的には十分な場合があります。float の比較のトピックを深く掘り下げたい場合は、この記事に必要以上の情報が含まれている可能性があります。イプシロン法については次のように述べています。
expectedResult の範囲がわかっている場合、絶対誤差のチェックは簡単で効果的です。絶対誤差値が、扱っている浮動小数点の範囲とタイプの最小表現可能な差よりも大きいことを確認してください。
いいえ、整数は浮動小数点数に正確にマッピングされるため、ユース ケースには問題はありません (たとえば 0.3 の場合のように、小数点以下の切り捨ての問題はありませんが、3 は1.1E10
2 進数の科学表記法です)。
私が考えることができる最悪のシナリオでは、2 つの連続する float 値の間に 1 より大きい「ギャップ」があるため、float で表現できない整数が存在する可能性がありますが、その場合でも、整数が float にキャストされると比較を行うと、float リテラルと同じ方法で、最も近い float に切り捨てられます。
したがって、浮動小数点数が非 10 進リテラルから取得されている限り、同等の整数との比較は同じになります。これは、整数が比較を行う前にまったく同じ浮動小数点数にキャストされるためです。
コードが文字通り投稿したものと同じように見える場合(間に計算がない場合)、3.0
and (float)3
(整数は自動的にfloatに変換されるため)が同じであるかどうかが問題になります。3は正確にとして表現できるので、この場合は同じであることが保証されていると思いますfloat
。
余談ですが、整数がfloatとして正確に表現できない場合でも(つまり、整数が本当に大きい場合)、ほとんどの実装で同じだx.0
と思います。そうでない場合、コンパイラは最初(float)x
にどのように生成するのでしょうか。x.0
のようなことをするために(float)x
?ただし、これは規格では保証されていないと思います。
問題の核心は、10進数が基数10で有限表現を持つ浮動小数点数が、2進数の基数2で常に有限表現を持つとは限らないことです。
Game Developers Conference のレクチャーNumerical Robustness for Geometry Calculations (別名、EPSILON は 0.00001 ではありません!)に興味があるかもしれません。さまざまなタスクに適したしきい値/イプシロン値の選択について詳しく説明しています。
(別の回答で「すべてのコンピューター科学者が浮動小数点について知っておくべきこと」についての言及も+1。)
編集:
正しい方法は、次のepsilon
方法を使用することです。
#include <math.h>
int x = 3;
int y = 3.0;
if (fabs((float) x - y) < 0.0001) { // Adjust the epsilon
// Do stuff
}