+0 và -0 hiển thị hành vi khác nhau cho dữ liệu int và float


16

Tôi đã đọc bài này tiêu cực và tích cực bằng không .

Theo hiểu biết của tôi, mã sau đây sẽ cung cấp truetrue như là một đầu ra.

Tuy nhiên, nó là cho falsetruenhư một đầu ra.

Tôi đang so sánh số 0 âm với số 0 dương.

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

Tại sao chúng ta có hành vi khác nhau cho 0 IntegerFloat?


11
Nếu bạn kiểm tra javadocs, docs.oracle.com/javase/8/docs/api/java/lang/ trộm Định nghĩa cho phép các bảng băm hoạt động chính xác. Ngoài ra, không có số nguyên -0.
matt

@matt nếu -0 không phải là số nguyên thì nên đánh giá là sai ...
Joker

3
Khi bạn nói i2 = -i; i2 có đại diện bit chính xác của i, không có cách nào để phân biệt chúng. ii2hoàn toàn giống nhau Sau đó, khi bạn tạo Integers mới , cả hai đều bao bọc cùng một giá trị. I1.equals(I)sẽ đúng
matt

1
Hãy thử int i = Integer.MIN_VALUE, i2 = -i;...
Holger

1
Nhân tiện, không có lý do để sử dụng newcho các loại trình bao bọc ở đây. Sử dụng chỉ, ví dụ:Integer i = 0, i2 = -i; System.out.println(i.equals(i2)); Float f1 = 0f, f2 = -f1; System.out.println(f1.equals(f2));
Holger

Câu trả lời:


19

Ints và float là những con thú khá khác nhau trong Java. Ints được mã hóa dưới dạng bổ sung của hai , có một giá trị 0 duy nhất. Phao sử dụng IEEE 754 ( biến thể 32 bit cho phao và 64 bit cho đôi). IEEE 754 hơi phức tạp, nhưng với mục đích của câu trả lời này, bạn chỉ cần biết rằng nó có ba phần, phần đầu tiên là một bit dấu. Điều đó có nghĩa là đối với bất kỳ số float nào, có một biến thể tích cực và tiêu cực¹. Điều đó bao gồm 0, vì vậy số float thực sự có hai giá trị "zero", +0 và -0.

Bên cạnh đó, phần bổ sung của hai ints sử dụng không phải là cách duy nhất để mã hóa số nguyên trong khoa học máy tính. Có các phương pháp khác, như bổ sung của các phương thức, nhưng chúng có các điểm kỳ quặc - như có cả giá trị +0 và -0 là các giá trị riêng biệt. ;-)

Khi bạn so sánh các nguyên hàm float (và nhân đôi), Java coi +0 và -0 bằng nhau. Nhưng khi bạn đóng hộp chúng, Java sẽ xử lý chúng một cách riêng biệt, như được mô tả trong Float#equals. Điều này cho phép phương thức Equals phù hợp với cách hashCodetriển khai của chúng (cũng như compareTo), chỉ sử dụng các bit của float (bao gồm giá trị đã ký) và chuyển chúng như là thành một int.

Họ có thể đã chọn một số tùy chọn khác cho bằng / hashCode / so sánh, nhưng họ đã không làm thế. Tôi không chắc những gì đã xem xét thiết kế. Nhưng trong ít nhất một vấn đề, Float#equalsluôn luôn phân kỳ khỏi nguyên thủy trôi nổi ==: Trong nguyên thủy NaN != NaN, nhưng đối với tất cả các đối tượng, o.equals(o)cũng phải đúng . Điều đó có nghĩa là nếu bạn đã có Float f = Float.NaN, f.equals(f)mặc dù f.floatValue() != f.floatValue().


Values ​​Các giá trị NaN (không phải là số) có một bit dấu, nhưng nó không có ý nghĩa gì ngoài việc đặt hàng và Java bỏ qua nó (ngay cả để đặt hàng).


10

Đây là một trong những ngoại lệ tương đương Float

có hai trường hợp ngoại lệ:

Nếu F1 đại diện cho + 0,0f trong khi f2 đại diện cho -0.0f hoặc ngược lại, thử nghiệm bằng nhau có giá trị sai

Tại sao cũng được mô tả:

Định nghĩa này cho phép các bảng băm hoạt động đúng.

-0 và 0 sẽ được biểu diễn khác nhau bằng cách sử dụng bit Float 31:

Bit 31 (bit được chọn bởi mặt nạ 0x80000000) đại diện cho dấu của số dấu phẩy động.

Đây không phải là trường hợp trong Integer


Câu hỏi là tại sao? Đây có phải là quy tắc khó và nhanh mà chúng ta phải nhồi nhét :(
Joker

@Joker Đã thêm trích dẫn cho phép các bảng băm hoạt động chính xác
user7294900

4
Một phần quan trọng mà câu trả lời này (và javadoc) không đề cập đến là sự khác biệt là trong số float, +0 và -0 là các giá trị khác nhau - tương đương, nhưng khác nhau. Về cơ bản, phao có ba phần với chúng, và phần đầu tiên là một bit cho biết phao là dương hay âm. Đó không phải là trường hợp của ints (như được trình bày trong Java), chỉ có một giá trị 0 duy nhất.
yshavit

@yshavit Cảm ơn, bạn có thể vui lòng chia sẻ giống như một câu trả lời không
Joker

3
@Joker Bit 31 (bit được chọn bởi mặt nạ 0x80000000) đại diện cho dấu của số dấu phẩy động.
user7294900

5

Đối với các số nguyên, không có sự phân biệt giữa -0 và 0 cho các số nguyên vì nó sử dụng biểu diễn khen của Twos . Vì vậy, ví dụ số nguyên của bạn ii1hoàn toàn giống nhau.

Đối với các float, có một đại diện -0 và giá trị của nó tương đương với 0, nhưng đại diện bit thì khác. Do đó Float mới (0f) và Float mới (-0f) sẽ có các đại diện khác nhau.

Bạn có thể thấy sự khác biệt trong các biểu diễn bit.

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

-2147483648, 0

Và nếu bạn rời khỏi fđể khai báo -0fthì nó sẽ được coi là một số nguyên và bạn sẽ không thấy bất kỳ sự khác biệt nào trong đầu ra.


Tuy nhiên, phao nguyên thủy dường như hoạt động tốt với điều đó. Đó là 0.0f == -0.0f. Vì vậy, các hành vi khác nhau chỉ trong java.lang.Float.
ngà

3
@ivant theo IEEE754, "Tuy nhiên, các hoạt động so sánh bình thường, coi NaN là không có thứ tự và so sánh −0 và +0 như nhau" en.m.wikipedia.org/wiki/IEEE_754
Andy Turner

@AndyTurner, vâng tôi hiểu điều đó. Tôi chỉ chỉ ra rằng trong Java có một sự khác biệt trong hành vi giữa kiểu nguyên thủy float, phù hợp với IEEE754 về vấn đề này và java.lang.Float, thì không. Vì vậy, chỉ sự khác biệt trong biểu diễn bit là không đủ để giải thích điều này.
ngà
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.