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

为什么我们对于Integer和的0会有不同的行为Float


11
如果检查javadocs,则为docs.oracle.com/javase/8/docs/api/java/lang/…该定义允许哈希表正常运行。另外,没有-0整数。
马特

@matt如果-0不是整数,则应将其评估为假...
Joker

3
当你说i2 = -i; i2采用i的确切位表示形式,无法识别它们。i并且i2完全一样。然后,当您创建new时,Integer它们都会包装完全相同的值。I1.equals(I)会是真的。
马特

1
试试int i = Integer.MIN_VALUE, i2 = -i;……
Holger

1
顺便说一下,这里没有理由使用new包装类型。只需使用,例如Integer i = 0, i2 = -i; System.out.println(i.equals(i2)); Float f1 = 0f, f2 = -f1; System.out.println(f1.equals(f2));
Holger

Answers:


19

在Java中,整数和浮点数是完全不同的野兽。整数被编码为二进制补码(具有单个0值)。浮点数使用IEEE 754(浮点数为32 ,双精度64位)。IEEE 754有点复杂,但是出于这个答案的目的,您只需要知道它包含三个部分,第一个部分是符号位。这意味着对于任何浮动货币,都有正面和负面的变化¹。它包括0,所以浮点数实际上有两个“零”值,即+0和-0。

顺便说一句,int使用的二进制补码并不是计算机科学中编码整数的唯一方法。还有其他方法,例如's'的complement,但是它们也有一些古怪之处,例如+0和-0都是不同的值。;-)

比较浮点原语(和双精度)时,Java将+0和-0视为相等。但是,当您将它们装箱时,Java会分别对待它们,如中所述Float#equals。这使equals方法与其hashCode实现(以及compareTo)保持一致,该实现仅使用float的位(包括该带符号的值)并将它们按原样推入int。

他们本可以为equals / hashCode / compareTo选择其他选项,但没有。我不确定当时的设计考虑因素是什么。但是至少在某一方面,Float#equals总是会与float基本类型有所不同==:在基本类型中NaN != NaN,但对于所有对象,o.equals(o)也必须为true。那意味着如果你有Float f = Float.NaNf.equals(f)即使f.floatValue() != f.floatValue()


¹NaN(非数字)值具有符号位,但是除了用于排序外,它没有其他含义,Java会忽略它(甚至用于排序)。


10

这是浮动等于例外之一

有两个例外:

如果f1代表+ 0.0f,而f2代表-0.0f,反之亦然,则相等测试的值为false

为什么也被描述:

此定义允许哈希表正常运行。

-0和0将使用Float的第31位来表示不同:

位31(由掩码0x80000000选择的位)表示浮点数的符号。

情况并非如此 Integer


问题是为什么?这是我们必须严格遵守的规则吗:(
小丑

@Joker添加了引号,允许哈希表正常运行
user7294900

4
这个答案(和Javadoc)没有提到的关键之处在于,区别在于浮点数中+0和-0是不同的值-相等,但不同。浮点数基本上由三部分组成,第一部分是一点,它表示浮点数是正数还是负数。这是为整数(如在Java中表示)的情况下,仅具有单一的0值。
yshavit

@yshavit谢谢,能否请您与我分享答案
Joker

3
@Joker 位31(由掩码0x80000000选择的位)表示浮点数的符号。
user7294900

5

对于整数,整数-0和0之间没有区别,因为它使用二进制补码表示形式。因此,您的整数示例ii1完全相同。

对于浮点数,有-0表示形式,其值等于0,但位表示形式不同。因此,新的Float(0f)和新的Float(-0f)将具有不同的表示形式。

您可以看到位表示形式的差异。

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

-2147483648,0

而且,如果不f声明,-0f则将其视为整数,并且输出不会有任何差异。


但是原始的float似乎可以正常工作。那是0.0f == -0.0f。因此,不同的行为仅存在于中java.lang.Float
ivant

3
@ivant根据IEEE754,“但是,正常的比较操作将NaN视为无序,并且将-0和+0视为相等”,en.m.wikipedia.org / wiki / IEEE_754
Andy Turner

@AndyTurner,是的,我明白。我只是指出,在Java中,原始类型float(在这方面符合IEEE754)与行为没有区别java.lang.Float。因此,仅比特表示的差异不足以解释这一点。
ivant
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.