このjava.sunページ によると==
、Javaの浮動小数点数の等値比較演算子です。
ただし、このコードを入力すると:
if(sectionID == currentSectionID)
私のエディタに入って静的分析を実行すると、次のようになります。
==
浮動小数点値を比較するために使用することの何が問題になっていますか? それを行う正しい方法は何ですか?
このjava.sunページ によると==
、Javaの浮動小数点数の等値比較演算子です。
ただし、このコードを入力すると:
if(sectionID == currentSectionID)
私のエディタに入って静的分析を実行すると、次のようになります。
==
浮動小数点値を比較するために使用することの何が問題になっていますか? それを行う正しい方法は何ですか?
float が「等しい」かどうかをテストする正しい方法は次のとおりです。
if(Math.abs(sectionID - currentSectionID) < epsilon)
ここで、epsilon は、目的の精度に応じて、0.00000001 のような非常に小さい数値です。
浮動小数点値は少しずれている可能性があるため、正確に等しいと報告されない場合があります。たとえば、float を「6.1」に設定して再度出力すると、「6.099999904632568359375」のような値が報告される場合があります。これは、フロートが機能する方法の基本です。したがって、等式を使用してそれらを比較するのではなく、範囲内で比較する必要があります。つまり、浮動小数点数と比較したい数値との差が特定の絶対値よりも小さい場合です。
レジスターに関するこの記事では、これが事実である理由の概要を説明しています。有益で興味深い読書。
他の人が言っていることの背後にある理由を説明するだけです。
float の 2 進数表現はちょっと厄介です。
バイナリでは、ほとんどのプログラマーは 1b=1d、10b=2d、100b=4d、1000b=8d の相関関係を知っています。
まあ、それは他の方法でも機能します。
.1b=.5d、.01b=.25d、.001b=.125、...
問題は、.1、.2、.3 などのほとんどの 10 進数を正確に表す方法がないことです。できることは、2 進数で概算することだけです。.10000000000001 または .999999999999 の代わりに .1 を表示するように、システムは数値を出力するときに少し丸め処理を行います (これらはおそらく .1 と同じくらい保存された表現に近いものです)。
コメントから編集: これが問題である理由は、私たちの期待です。2/3 が .7 または .67 または .666667 のいずれかの 10 進数に変換されるときに、ある時点でファッジされることを完全に期待しています。 ――そして、まさにそれが起こっているのです。
ちなみに、内部に格納されている数値は、バイナリの「科学表記法」を使用した純粋なバイナリ表現です。したがって、10 進数の 10.75d を格納するように指示すると、10 には 1010b が格納され、10 進数には .11b が格納されます。したがって、.101011 を保存し、最後にいくつかのビットを保存して、次のように言います。小数点を 4 桁右に移動します。
(技術的には小数点ではなくなりましたが、現在は 2 進小数点になっていますが、この用語を使用しても、この答えが役に立たないほとんどの人にとって理解しやすいものにはなりませんでした。)
== を使用して浮動小数点値を比較することの何が問題になっていますか?
それは真実ではないからです0.1 + 0.2 == 0.3
float (および double) に関しては多くの混乱があると思いますが、それを解決するのは良いことです。
標準準拠の JVM [*]でID として float を使用することは、本質的に悪いことではありません。float ID を単に x に設定し、何もせず (つまり、算術演算を行わず)、後で y == x をテストすれば問題ありません。また、それらを HashMap のキーとして使用しても問題はありません。できないことはx == (x - y) + y
、 などの等式を想定することです。そうは言っても、人々は通常、整数型を ID として使用します。ここにいるほとんどの人はこのコードに嫌気がさしていることがわかります。したがって、実際的な理由から、慣習に従う方がよいでしょう。 . longと同じ数の異なるdouble
値があることに注意してください。values
したがって、 を使用しても何も得られませんdouble
。また、「次に使用可能な ID」を生成することは、倍精度浮動小数点数では扱いにくい場合があり、浮動小数点演算の知識が必要です。苦労する価値はありません。
一方、2 つの数学的に同等な計算の結果が数値的に等しいことに依存するのは危険です。これは、10 進数から 2 進数に変換する際の丸め誤差と精度の低下が原因です。これは、SOで死ぬまで議論されています。
[*] 私が「標準準拠の JVM」と言ったとき、特定の脳に損傷を受けた JVM 実装を除外したかったのです。これを参照してください。
丸め誤差のため、浮動小数点値は信頼できません。
そのため、sectionID などのキー値として使用しないでください。代わりに整数を使用するか、十分な値が含まれていない場合long
。int
これは、Java に固有の問題ではありません。== を使用して 2 つの浮動小数点数/倍精度浮動小数点数/10 進数型の数値を比較すると、格納方法が原因で問題が発生する可能性があります。単精度浮動小数点 (IEEE 標準 754 による) は 32 ビットで、次のように分散されます。
1 ビット - 符号 (0 = 正、1 = 負)
8 ビット - 指数 (2^x の x の特別な (バイアス
-127) 表現) 23 ビット - 仮数。格納されている実際の数。
仮数が問題の原因です。科学表記法のようなもので、基数 2 (バイナリ) の数値のみが 1.110011 x 2^5 のように見えます。ただし、バイナリでは、最初の 1 は常に 1 です (0 の表現を除く)。
したがって、メモリ空間を少し節約するために (しゃれが意図されています)、IEEE は 1 を想定することを決定しました。たとえば、1011 の仮数は実際には 1.1011 です。
0 は float では正確に表現できない可能性があるため、特に 0 の比較で問題が発生する可能性があります。これが、他の回答で説明されている浮動小数点数の問題に加えて、 == が推奨されない主な理由です。
Java には、言語が多くの異なるプラットフォーム間で共通であり、それぞれが独自の float 形式を持つ可能性があるという固有の問題があります。これにより、== を避けることがさらに重要になります。
2 つのフロート (言語固有ではないことに注意してください) が等しいかどうかを比較する適切な方法は次のとおりです。
if(ABS(float1 - float2) < ACCEPTABLE_ERROR)
//they are approximately equal
ここで、ACCEPTABLE_ERROR は #defined または 0.000000001 に等しいその他の定数、またはビクターが既に述べたように必要な精度です。
一部の言語には、この機能またはこの定数が組み込まれていますが、一般的に、これは良い習慣です。
-0.0f
以前の回答に加えて、 and +0.0f
(それらはそうではあり==
ませんequals
)およびFloat.NaN
(そうではありequals
ません)に関連する奇妙な動作があることに注意する必要があります==
(私がそれを正しく理解していることを願っています-ああ、それをしないでください!)。
編集:確認しましょう!
import static java.lang.Float.NaN;
public class Fl {
public static void main(String[] args) {
System.err.println( -0.0f == 0.0f); // true
System.err.println(new Float(-0.0f).equals(new Float(0.0f))); // false
System.err.println( NaN == NaN); // false
System.err.println(new Float( NaN).equals(new Float( NaN))); // true
}
}
IEEE/754へようこそ。
以下は、これと遭遇する可能性のある他の多くの浮動小数点の問題に関する非常に長い (しかし役立つことを願っています) 議論です:すべてのコンピュータ科学者が浮動小数点演算について知っておくべきこと
まず第一に、彼らはフロートですか、それともフロートですか?それらの 1 つが Float の場合は、equals() メソッドを使用する必要があります。また、おそらく静的な Float.compare メソッドを使用するのが最善です。
Float.floatToIntBits() を使用できます。
Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)
フロートを*使用する*必要がある場合は、strictfp キーワードが役立つ場合があります。
== にしたいかもしれませんが、123.4444444444443 != 123.4444444444442
等しい実数を生成する 2 つの異なる計算が、必ずしも等しい浮動小数点数を生成するとは限りません。== を使用して計算結果を比較する人は通常、これに驚かされることになります。そのため、警告は、他の方法では微妙で再現が難しいバグにフラグを立てるのに役立ちます。
sectionID および currentSectionID という名前のものにフロートを使用するアウトソーシング コードを扱っていますか? ちょっと興味があるんだけど。
@Bill K: 「フロートのバイナリ表現はちょっと面倒です。」どうして?どうすればもっとうまくできますか?終わらないため、どの基数でも適切に表すことができない特定の数があります。パイが良い例です。近似することしかできません。より良い解決策がある場合は、Intel にお問い合わせください。
私が言える1行の答えでは、次を使用する必要があります。
Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)
関連する演算子を正しく使用する方法をさらに学習できるように、ここでいくつかのケースについて詳しく説明します。 一般に、Java で文字列をテストするには 3 つの方法があります。==、.equals ()、または Objects.equals () を使用できます。
それらはどう違いますか?== は、文字列の参照品質をテストします。つまり、2 つのオブジェクトが同じかどうかを調べます。一方、.equals () は、2 つの文字列が論理的に等しいかどうかをテストします。最後に、Objects.equals () は 2 つの文字列に null があるかどうかをテストし、.equals () を呼び出すかどうかを決定します。
使用する理想的な演算子
3 つのオペレーターにはそれぞれ独自の長所と短所があるため、これは多くの議論の対象となっています。たとえば、オブジェクト参照を比較する場合は == が好まれることが多いですが、文字列値も比較しているように見える場合があります。
ただし、Java は値を比較しているように錯覚させますが、実際にはそうではないため、得られるのは値の低下です。以下の 2 つのケースを考えてみましょう。
String a="Test";
String b="Test";
if(a==b) ===> true
String nullString1 = null;
String nullString2 = null;
//evaluates to true
nullString1 == nullString2;
//throws an exception
nullString1.equals(nullString2);
そのため、設計された特定の属性をテストするときは、各演算子を使用する方がはるかに優れています。しかし、ほとんどの場合、Objects.equals () はより普遍的な演算子であるため、経験豊富な Web 開発者はそれを選択します。
ここで詳細を取得できます: http://fluentthemes.com/use-compare-strings-java/
丸め誤差を減らす1つの方法は、floatではなくdoubleを使用することです。これで問題が解決することはありませんが、プログラムのエラーの量が減り、floatが最良の選択になることはほとんどありません。私見では。
正しい方法は
java.lang.Float.compare(float1, float2)