+0 dan -0 menunjukkan perilaku berbeda untuk data int dan float

16

Saya telah membaca posting ini nol negatif dan positif .

Untuk pemahaman saya, kode berikut harus memberi true dan true sebagai output.

Namun, itu memberi falsedan truesebagai hasil.

Saya membandingkan nol negatif dengan nol positif.

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));
      }
  }

Mengapa kita memiliki perilaku berbeda untuk 0 untuk Integerdan Float?

Pelawak
sumber
11
Jika Anda memeriksa javadocs, docs.oracle.com/javase/8/docs/api/java/lang/… Definisi ini memungkinkan tabel hash berfungsi dengan baik. Juga, tidak ada -0 bilangan bulat.
matt
@matt jika -0 bukan bilangan bulat maka harus dievaluasi sebagai salah ...
Joker
3
Ketika Anda mengatakan i2 = -i; i2 mengambil representasi bit yang tepat dari i, tidak ada cara untuk membedakannya. idan i2persis sama. Kemudian ketika Anda membuat Integers baru , keduanya membungkus nilai yang sama persis. I1.equals(I)akan benar.
matt
1
Coba int i = Integer.MIN_VALUE, i2 = -i;...
Holger
1
Omong-omong, tidak ada alasan untuk menggunakan newjenis pembungkus di sini. Cukup gunakan, misalnyaInteger i = 0, i2 = -i; System.out.println(i.equals(i2)); Float f1 = 0f, f2 = -f1; System.out.println(f1.equals(f2));
Holger

Jawaban:

19

Ints dan float adalah binatang yang sangat berbeda di Jawa. Ints dikodekan sebagai komplemen dua , yang memiliki nilai 0 tunggal. Float menggunakan IEEE 754 ( varian 32-bit untuk float, dan 64-bit untuk ganda). IEEE 754 agak rumit, tetapi untuk tujuan jawaban ini, Anda hanya perlu tahu bahwa ia memiliki tiga bagian, yang pertama adalah bit tanda. Itu berarti untuk pelampung apa pun, ada varian positif dan negatif¹. Itu termasuk 0, jadi floats sebenarnya memiliki dua nilai "nol", +0 dan -0.

Sebagai tambahan, komplemen kedua yang digunakan int bukan satu-satunya cara untuk menyandikan bilangan bulat dalam ilmu komputer. Ada metode lain, seperti komplemen yang ' , tetapi mereka memiliki kebiasaan - seperti memiliki kedua +0 dan -0 sebagai nilai yang berbeda. ;-)

Saat Anda membandingkan float primitif (dan ganda), Java memperlakukan +0 dan -0 sama. Tetapi ketika Anda kotak mereka, Jawa memperlakukan mereka secara terpisah, seperti yang dijelaskan dalam Float#equals. Ini memungkinkan metode equals konsisten dengan hashCodeimplementasinya (dan juga compareTo), yang hanya menggunakan bit float (termasuk nilai yang ditandatangani) dan mendorong mereka apa adanya ke int.

Mereka bisa saja memilih beberapa opsi lain untuk equals / hashCode / compareTo, tetapi mereka tidak melakukannya. Saya tidak yakin apa pertimbangan desainnya. Tetapi setidaknya dalam satu hal, Float#equalsselalu akan menyimpang dari float primitive ==: Dalam primitif NaN != NaN, tetapi untuk semua objek, o.equals(o)juga harus benar . Itu artinya jika sudah Float f = Float.NaN, maka f.equals(f)sekalipun f.floatValue() != f.floatValue().


Values ​​Nilai NaN (bukan angka) memiliki bit tanda, tetapi tidak memiliki arti selain untuk pemesanan, dan Java mengabaikannya (bahkan untuk pemesanan).

yshavit
sumber
10

Ini adalah salah satu dari Float sama dengan pengecualian

ada dua pengecualian:

Jika f1 mewakili + 0.0f sedangkan f2 mewakili -0.0f , atau sebaliknya, pengujian yang sama memiliki nilai false

Mengapa dijelaskan juga:

Definisi ini memungkinkan tabel hash untuk beroperasi dengan benar.

-0 dan 0 akan direpresentasikan secara berbeda menggunakan bit Float 31:

Bit 31 (bit yang dipilih oleh mask 0x80000000) mewakili tanda angka floating-point.

Ini bukan masalahnya Integer

pengguna7294900
sumber
pertanyaannya kenapa? Apakah ini aturan yang keras dan cepat yang harus kita cram :(
Joker
@ Joker Menambahkan kutipan memungkinkan tabel hash untuk beroperasi dengan benar
user7294900
4
Bagian penting yang tidak dijawab oleh jawaban ini (dan javadoc) adalah bahwa perbedaannya adalah pada float, +0 dan -0 adalah nilai yang berbeda - sama, tetapi berbeda. Pada dasarnya, float memiliki tiga bagian untuk mereka, dan bagian pertama adalah bit tunggal yang mengatakan apakah float itu positif atau negatif. Itu bukan kasus untuk ints (seperti diwakili di Jawa), yang hanya memiliki nilai 0 tunggal.
yshavit
@yshavit Terima kasih, bisakah Anda membagikan hal yang sama sebagai jawaban
Joker
3
@Joker Bit 31 (bit yang dipilih oleh mask 0x80000000) mewakili tanda angka floating-point.
user7294900
5

Untuk bilangan bulat, tidak ada perbedaan antara -0 dan 0 untuk bilangan bulat karena menggunakan representasi pujian Twos . Jadi contoh integer Anda idan i1persis sama.

Untuk mengapung, ada representasi -0, dan nilainya setara dengan 0, tetapi representasi bit berbeda. Maka Float baru (0f) dan Float baru (-0f) akan memiliki representasi yang berbeda.

Anda dapat melihat perbedaan dalam representasi bit.

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

-2147483648, 0

Dan jika Anda membiarkan funtuk menyatakan -0fmaka itu akan diperlakukan sebagai bilangan bulat, dan Anda tidak akan melihat perbedaan dalam output.

matt
sumber
Namun pelampung primitif tampaknya berfungsi baik dengan itu. Yaitu 0.0f == -0.0f. Jadi perilaku yang berbeda hanya ada di java.lang.Float.
ivant
3
@Vant menurut IEEE754, "Namun, operasi perbandingan normal memperlakukan NaN sebagai tidak berurutan dan membandingkan −0 dan +0 sama dengan" en.m.wikipedia.org/wiki/IEEE_754
Andy Turner
@ AndyTurner, ya saya mengerti itu. Saya hanya menunjukkan bahwa di Jawa ada perbedaan dalam perilaku antara tipe primitif float, yang sesuai dengan IEEE754 dalam hal ini dan java.lang.Float, yang tidak. Jadi hanya perbedaan dalam representasi bit tidak cukup untuk menjelaskan hal ini.
ivant