+0および-0は、intおよびfloatデータの異なる動作を示します


16

私はこの記事を読んで、マイナスとプラスのゼロです。

私の理解では、次のコードは 出力としてtruetrueを与えるはずです。

しかし、それは与えているfalsetrue出力など。

負のゼロと正のゼロを比較しています。

public class Test {
     public static void main(String[] args) {
            float f = 0;
            float f2 = -f;
            Float F = new Float(f);
            Float F1 = new Float(f2);
            System.out.println(F1.equals(F));

            int i = 0;
            int i2 = -i;
            Integer I = new Integer(i);
            Integer I1 = new Integer(i2);
            System.out.println(I1.equals(I));
      }
  }

なぜ0のための異なる振る舞いを持っていますIntegerFloat


11
javadocs、docs.oracle.com / javase / 8 / docs / api / java / lang /…を確認すると、ハッシュテーブルが正しく機能するように定義されています。また、-0整数はありません。
マット

@matt -0が整数でない場合、falseと評価されます...
Joker

3
あなたが言うときi2 = -i; i2はiの正確なビット表現を取り、それらを識別する方法はありません。ii2まったく同じです。次に、新しいを作成するとInteger、どちらもまったく同じ値をラップします。I1.equals(I)本当でしょう。
マット

1
試してみてくださいint i = Integer.MIN_VALUE, i2 = -i;...
ホルガー

1
ちなみに、newここではラッパータイプを使用する理由はありません。ただ、使用、例えばInteger i = 0, i2 = -i; System.out.println(i.equals(i2)); Float f1 = 0f, f2 = -f1; System.out.println(f1.equals(f2));
ホルガー

回答:


19

Javaでは、Intとfloatはかなり異なります。Intは、単一の0値を持つ2の補数としてエンコードされます。FloatはIEEE 754を使用します(floatの場合は32ビット版、doubleの場合は64 ビット版)。IEEE 754はいくぶん複雑ですが、この回答の目的のために、3つのセクションがあることを知っておく必要があります。最初のセクションは符号ビットです。つまり、どのフロートにも、正と負のバリアントがあります¹。これには0が含まれるため、浮動小数点数には実際には+0と-0の2つの「ゼロ」値があります。

余談ですが、intが使用する2の補数は、コンピュータサイエンスで整数をエンコードする唯一の方法ではありません。1の補数のよう他の方法もありますが、+ 0と-0の両方を異なる値として持つなど、癖があります。;-)

floatプリミティブ(およびdouble)を比較すると、Javaは+0と-0を同じものとして扱います。ただし、それらをボックス化すると、Javaはで説明されているように、それらを個別に扱いますFloat#equals。これにより、equalsメソッドはfloatのビット(その符号付きの値を含む)を使用し、そのままintに表示するhashCode実装(およびcompareTo)と一致します。

equals / hashCode / compareToに他のオプションを選択することもできましたが、選択しませんでした。どのような設計上の考慮事項があったのかわかりません。しかし、少なくとも1つの点で、Float#equals常にfloatプリミティブから分岐していました==。プリミティブでNaN != NaNは、すべてのオブジェクトについて、o.equals(o)true でなければなりません。つまり、あなたが持っていたFloat f = Float.NaNとしてf.equals(f)も、それでもf.floatValue() != f.floatValue()


¹NaN(非数)値には符号ビットがありますが、順序付け以外の意味はなく、Javaはそれを無視します(順序付けの場合でも)。


10

これはFloat equals exceptionの 1つです

2つの例外があります。

f1が+ 0.0fを表しf2が-0.0fを表す場合、またはその逆の場合、等価テストの値はfalseです。

理由も説明されています:

この定義により、ハッシュテーブルが適切に動作します。

-0と0は、Floatのビット31を使用して異なって表現されます。

ビット31(マスク0x80000000によって選択されるビット)は、浮動小数点数の符号を表します。

これはそうではありません Integer


質問はなぜですか?この厳しくて速いルールは、私たちが詰め込まなければならないことですか:(
Joker

@Jokerは、ハッシュテーブルが適切に動作することを可能に
user7294900

4
この回答(およびjavadoc)で言及されていない重要な点は、フロートでは+0と-0は異なる値であることです。つまり、同等ですが異なります。基本的に、フロートには3つの部分があり、最初の部分は、フロートが正か負かを示す1ビットです。これ、(Javaで表される)intには当てはまりません。これは、単一の0値のみを持ちます。
yshavit

@yshavitありがとう、答えと同じものを教えてください
Joker

3
@Joker ビット31(マスク0x80000000によって選択されるビット)は、浮動小数点数の符号を表します。
user7294900

5

整数の場合、2の補数表現を使用するため、整数の-0と0は区別されません。したがって、整数の例ii1まったく同じです。

floatの場合、-0表現があり、その値は0と同等ですが、ビット表現は異なります。したがって、新しいFloat(0f)と新しいFloat(-0f)の表現は異なります。

ビット表現の違いがわかります。

System.out.println(Float.floatToIntBits(-0f) + ", " + Float.floatToIntBits(0f));

-2147483648、0

そして、fを宣言せずに宣言する-0fと、整数として扱われ、出力に違いが見られなくなります。


それでも、プリミティブフロートはそれでうまく機能するようです。それです0.0f == -0.0f。したがって、異なる動作はにのみありjava.lang.Floatます。
ivant

3
IEEE754による@ivant、「ただし、通常の比較演算はNaNを順序付けなしとして扱い、-0と+0を等しいものとして比較する」en.m.wikipedia.org/wiki/IEEE_754
Andy Turner

@AndyTurner、はい、私はそれを理解しています。Javaではfloat、この点でIEEE754に準拠しているプリミティブ型と準拠しjava.lang.Floatていないプリミティブ型の動作に違いがあることを指摘しています。したがって、ビット表現の違いだけではこれを説明するには不十分です。
ivant
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.