Я прочитал этот пост отрицательный и положительный ноль .
В моем понимании следующий код должен давать true
и true
в качестве вывода.
Тем не менее, это дает false
и true
в качестве вывода.
Я сравниваю отрицательный ноль с положительным нулем.
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 Integer
и Float
?
i
иi2
точно так же. Затем, когда вы создаете новыеInteger
s, они оба переносят одно и то же значение.I1.equals(I)
будет правдой.int i = Integer.MIN_VALUE, i2 = -i;
...new
типы обёрток. Просто используйте, например,Integer i = 0, i2 = -i; System.out.println(i.equals(i2)); Float f1 = 0f, f2 = -f1; System.out.println(f1.equals(f2));
Ответы:
Интты и поплавки - это довольно разные звери в Java. Интты кодируются как два дополнения , которое имеет одно значение 0. Float использует IEEE 754 ( 32-битный вариант для float и 64-битный для double). IEEE 754 несколько сложен, но для ответа на этот вопрос вам просто нужно знать, что он состоит из трех разделов, первый из которых является знаковым битом. Это означает, что для любого поплавка есть положительный и отрицательный варианты. Это включает в себя 0, так что плавающие на самом деле имеют два «нулевых» значения, +0 и -0.
Кроме того, дополнение к двум, используемое целыми числами, не единственный способ кодировать целые числа в компьютерной науке. Существуют и другие методы, например , дополнение к ним , но у них есть свои причуды, например, наличие как +0, так и -0 в качестве различных значений. ;-)
Когда вы сравниваете примитивы с плавающей точкой (и удваивает), Java обрабатывает +0 и -0 как равные. Но когда вы их упаковываете, Java обрабатывает их отдельно, как описано в
Float#equals
. Это позволяет методу equals быть совместимым с ихhashCode
реализацией (а такжеcompareTo
), который просто использует биты с плавающей точкой (включая значение со знаком) и помещает их как есть в int.Они могли бы выбрать другой вариант для equals / hashCode / compareTo, но они этого не сделали. Я не уверен, какие были соображения дизайна. Но, по крайней мере, в одном отношении
Float#equals
всегда собирались расходиться с примитивами поплавка==
: в примитивахNaN != NaN
, но для всех объектов,o.equals(o)
также должно быть верно . Это означает, что если у вас естьFloat f = Float.NaN
, тоf.equals(f)
даже еслиf.floatValue() != f.floatValue()
.¹ Значения NaN (not-a-number) имеют знаковый бит, но он не имеет никакого значения, кроме как для упорядочения, а Java игнорирует его (даже для упорядочения).
источник
Это одно из исключений с плавающей точкой
Почему также описано:
-0 и 0 будут представлены по-разному с помощью бита Float 31:
Это не так в
Integer
источник
Для целых чисел нет различия между -0 и 0 для целых чисел, потому что он использует представление комплимента Twos . Так что ваш пример целочисленный
i
иi1
точно такой же.Для чисел с плавающей запятой существует представление -0, и его значение эквивалентно 0, но представление битов отличается. Следовательно, новый Float (0f) и новый Float (-0f) будут иметь разные представления.
Вы можете увидеть разницу в битовых представлениях.
И если вы не укажете
f
объявление,-0f
то оно будет рассматриваться как целое число, и вы не увидите никакой разницы в выводе.источник
0.0f == -0.0f
. Так что другое поведение только вjava.lang.Float
.float
, который соответствует IEEE754 в этом отношении, иjava.lang.Float
, который не делает. Так что просто разницы в представлении битов недостаточно, чтобы объяснить это.