このjava.sunページによると ==
、Javaの浮動小数点数の等価比較演算子です。
ただし、このコードを入力すると:
if(sectionID == currentSectionID)
エディターに入力して静的分析を実行すると、「==と比較したJAVA0078浮動小数点値」が表示されます。
を使用==
して浮動小数点値を比較することの何が問題になっていますか?それを行う正しい方法は何ですか?
このjava.sunページによると ==
、Javaの浮動小数点数の等価比較演算子です。
ただし、このコードを入力すると:
if(sectionID == currentSectionID)
エディターに入力して静的分析を実行すると、「==と比較したJAVA0078浮動小数点値」が表示されます。
を使用==
して浮動小数点値を比較することの何が問題になっていますか?それを行う正しい方法は何ですか?
回答:
floatが「等しい」かどうかをテストする正しい方法は次のとおりです。
if(Math.abs(sectionID - currentSectionID) < epsilon)
ここで、イプシロンは、希望する精度に応じて、0.00000001のような非常に小さな数値です。
if(Math.abs(sectionID - currentSectionID) < epsilon*sectionID
、その問題に取り組むために関数をに変更できますか?
Math.ulp()
この質問への私の回答で使用することを提案しました。
浮動小数点値は少しずれている可能性があるため、正確に等しいとは報告されない場合があります。たとえば、フロートを「6.1」に設定してからもう一度印刷すると、「6.099999904632568359375」のような値が報告されることがあります。これは、フロートが機能する方法の基本です。したがって、同等性を使用してそれらを比較するのではなく、範囲内で比較する必要があります。つまり、フロートと比較する数値の差が特定の絶対値より小さい場合です。
登録に関するこの記事では、これが当てはまる理由の概要を説明しています。有用で興味深い読書。
他の誰もが言っていることの背後にある理由を述べるためだけに。
フロートのバイナリ表現は、ちょっと面倒です。
バイナリでは、ほとんどのプログラマーは1b = 1d、10b = 2d、100b = 4d、1000b = 8d間の相関関係を知っています。
まあそれは逆にも働きます。
.1b = .5d、.01b = .25d、.001b = .125、...
問題は、.1、.2、.3などのほとんどの10進数を表す正確な方法がないことです。できるのは、バイナリで近似することだけです。システムは、数値が印刷されるときに少しファッジ丸めを行って、.10000000000001または.999999999999(おそらく.1と同じくらい格納された表現に近い)の代わりに.1を表示します。
コメントから編集:これが問題である理由は私たちの期待です。2/3は、10進数に変換するときに、ある時点で.7または.67または.666667のいずれかであると完全に予測されます。しかし、.1が2/3と同じ方法で丸められるとは自動的には期待していません。 -そしてそれがまさに起こっていることです。
ちなみに、もし興味があれば、内部に保存する数値は、バイナリの「科学表記法」を使用した純粋なバイナリ表現です。したがって、10進数の10.75dを格納するように指示した場合、10の場合は1010b、10進数の場合は.11bが格納されます。したがって、.101011が格納され、最後に数ビットが節約されます。つまり、小数点を4桁右に移動します。
(技術的には小数点ではなくなりましたが、現在は2進小数点ですが、この用語を使用しても、この使用法の答えが見つかるほとんどの人にとって、理解が深まることはありません。)
==を使用して浮動小数点値を比較することの何が問題になっていますか?
それは真実ではないので 0.1 + 0.2 == 0.3
Float.compare(0.1f+0.2f, 0.3f) == 0
ですか?
浮動小数点数(および倍精度浮動小数点数)については多くの混乱があると思いますが、それを解消するのは良いことです。
標準に準拠したJVMで IDとしてフロートを使用することに本質的に問題はありません[*]。単にフロートIDをxに設定し、それを使用せず(つまり、算術演算を行わず)、後でy == xをテストすると、問題ありません。また、それらをHashMapのキーとして使用しても問題はありません。あなたができないことはのような等号を仮定することですx == (x - y) + y
。これは言われていますが、人々は通常整数型をIDとして使用し、ここのほとんどの人々はこのコードによって延期されているのを観察できます。 。double
long と同じ数の異なる値があるvalues
ため、を使用しても何も得られないことに注意してくださいdouble
。また、「次に利用可能なID」の生成は、倍精度浮動小数点数を使用すると扱いにくくなる可能性があり、浮動小数点演算の知識が必要です。トラブルの価値はありません。
一方、数学的に同等な2つの計算結果の数値の等価性に依存することは危険です。これは、10進数から2進数への変換時に丸め誤差と精度の低下が原因です。これはSOで死ぬまで議論されてきました。
[*]「標準準拠のJVM」と言ったとき、脳に損傷のある特定のJVM実装を除外したいと思いました。参照してくださいこれを。
==
する場合はequals
、ではなくを使用して比較されるように注意する必要があります。そうでない場合、それ自体と等しくないfloatがテーブルに格納されないようにします。そうでない場合、たとえば、さまざまな入力が与えられたときに式から生成できる一意の結果の数を数えようとするプログラムは、すべてのNaN値を一意と見なす場合があります。
Float
、を指すのではありませんfloat
。
Float
?一意のfloat
値のテーブルを作成し、それらをと比較しようとすると==
、恐ろしいIEEE-754比較ルールにより、テーブルがNaN
値でいっぱいになります。
float
タイプにはequals
メソッドがありません。
equals
インスタンスメソッドではなく、Float
typeの2つの値を比較する静的メソッド(クラス内だと思います)を意味していましたfloat
。
これは、Javaに固有の問題ではありません。==を使用して2つのfloat / doubles /任意の10進数型の数値を比較すると、それらの格納方法が原因で問題が発生する可能性があります。単精度浮動小数点数(IEEE標準754に準拠)は32ビットで、次のように分散されています。
1ビット-符号(0 =正、1 =負)
8ビット-指数(2 ^ xでのxの特殊(バイアス127)表現)
23ビット-マンティサ。保管されている実際の番号。
カマキリが問題の原因です。これはちょっと科学的な表記のようなもので、基数2(2進数)の数値のみが1.110011 x 2 ^ 5またはそれに似たもののように見えます。ただし、バイナリでは、最初の1は常に1です(0の表現を除く)
したがって、メモリスペースを少し節約するため(意図的にしゃれた)、IEEEは1を想定する必要があると決定しました。たとえば、1011のカマキリは実際には1.1011です。
これは、比較でいくつかの問題を引き起こす可能性があります。特に0は、floatで正確に表現できない可能性があるためです。これが、他の回答で説明されている浮動小数点演算の問題に加えて、==が推奨されない主な理由です。
Javaには、言語が多くの異なるプラットフォーム間で共通であり、それぞれが独自の固有の浮動小数点形式を持つことができるという固有の問題があります。つまり、==を避けることがさらに重要になります。
2つのフロート(言語固有ではない)が等しいかどうかを比較する適切な方法は、次のとおりです。
if(ABS(float1 - float2) < ACCEPTABLE_ERROR)
//they are approximately equal
ここで、ACCEPTABLE_ERRORは#definedであるか、0.000000001に等しい他の定数、またはビクターがすでに述べたように必要な精度です。
一部の言語には、この機能またはこの定数が組み込まれていますが、一般的にはこれが適切な習慣です。
今日のところ、それを行うための迅速で簡単な方法は次のとおりです。
if (Float.compare(sectionID, currentSectionID) == 0) {...}
ただし、ドキュメント ではマージンの差の値(イプシロン 、浮動小数点数の計算に常に存在 @Victorの回答からが、標準言語ライブラリの一部であるため、妥当なものでなければなりません。
さらに高い精度やカスタマイズされた精度が必要な場合は、
float epsilon = Float.MIN_NORMAL;
if(Math.abs(sectionID - currentSectionID) < epsilon){...}
別のソリューションオプションです。
(sectionId == currentSectionId)
ため、浮動小数点では正確ではありません。イプシロン法がより良いアプローチであり、これはこの答えにあります:stackoverflow.com/a/1088271/4212710
丸め誤差のため、浮動小数点値は信頼できません。
そのため、sectionIDなどのキー値として使用しないでください。代わりに整数を使用してください。またはlong
、int
十分な可能性のある値が含まれていない場合。
double
の方がはるかに正確ですが、浮動小数点値でもあるので、私の答えはfloat
との両方を含めることを意味していましたdouble
。
前回の回答に加えて、あなたは、関連付けられた奇妙な行動があることを認識すべきである-0.0f
と+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つだけです)。同様に、-0.0f
および0.0f
は異なるビットパターンで表されます(符号ビットは異なります)が、と等しい==
(ただしとは異なる)と比較しequals
ます。==
ビット比較であるというあなたの仮定は、一般的に言って、間違っています。
Float.floatToIntBits()を使用できます。
Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)
以下は自動的に最高の精度を使用します:
/**
* Compare to floats for (almost) equality. Will check whether they are
* at most 5 ULP apart.
*/
public static boolean isFloatingEqual(float v1, float v2) {
if (v1 == v2)
return true;
float absoluteDifference = Math.abs(v1 - v2);
float maxUlp = Math.max(Math.ulp(v1), Math.ulp(v2));
return absoluteDifference < 5 * maxUlp;
}
もちろん、ULPを5つより多くまたは少なく選択することもできます(「最後のユニット」)。
あなたは、Apache Commonsのライブラリに興味があれば、Precision
クラスが持っているcompareTo()
とequals()
イプシロンとULPの両方を持ちます。
double
をカバーするための要因として、10_000_000_000_000_000Lのようなものがさらに必要であるようです。
等しい実数を生成する2つの異なる計算は、必ずしも等しい浮動小数点数を生成するわけではありません。==を使用して計算結果を比較する人は通常これに驚かされるので、警告は、他の方法では微妙で再現が難しいバグを報告するのに役立ちます。
他の回答で述べたように、倍精度浮動小数点数にはわずかな偏差があります。そして、「許容できる」偏差を使用してそれらを比較する独自のメソッドを書くことができます。しかしながら ...
doubleを比較するためのapacheクラスがあります:org.apache.commons.math3.util.Precision
それはいくつかの興味深い定数を含みます:SAFE_MIN
そしてEPSILON
は、単純な算術演算の可能な最大の偏差です。
また、doubleの比較、等しい、または丸めに必要なメソッドも提供します。(ulpsまたは絶対偏差を使用)
私が言うことができる一行の答えでは、あなたは使うべきです:
Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)
関連する演算子を正しく使用する方法についてさらに学ぶために、ここでいくつかのケースについて詳しく説明します。一般に、Javaで文字列をテストするには3つの方法があります。== 、. equals()、またはObjects.equals()を使用できます。
それらはどう違いますか?==文字列の参照品質をテストして、2つのオブジェクトが同じであるかどうかを調べます。一方、.equals()は、2つの文字列の値が論理的に等しいかどうかをテストします。最後に、Objects.equals()は、2つの文字列のnullをテストしてから、.equals()を呼び出すかどうかを決定します。
使用するのに理想的なオペレーター
3つの演算子にはそれぞれ独自の長所と短所があるため、これは多くの議論の的となってきました。たとえば、==はオブジェクト参照を比較するときによく使用されるオプションですが、文字列値も比較しているように見える場合があります。
ただし、Javaは値を比較しているように見えますが、実際の意味ではそうではないので、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/
正しい方法は
java.lang.Float.compare(float1, float2)
Float.compare(1.1 + 2.2, 3.3) != 0