签名零双等于(在==中)但Double.compare(double,double)!= 0

时间:2017-08-07 09:57:07

标签: java

我在比较双零时遇到了一些奇怪的事情。根据双零基元的启动方式,Double.compare(double,double)方法可能会或可能不会认为它们“相等”(可能会也可能不会返回0)。

比较各种零双:使用==总是将它们报告为相等。如果它们在==方面相等,则它们必须(应该)在比较方法上相等。他们不是!

查看此示例程序:

public class CompareZeros {

public static void main(final String[] args) {

    final double negDbl = -0.0;
    final double posInt = 0;
    final double posDbl = 0.0;
    final double negInt = -0;

    CompareZeros.compare("negDbl <-> posInt", negDbl, posInt);
    CompareZeros.compare("negDbl <-> posDbl", negDbl, posDbl);
    CompareZeros.compare("negDbl <-> negInt", negDbl, negInt);

    CompareZeros.compare("posInt <-> negDbl", posInt, negDbl);
    CompareZeros.compare("posInt <-> posDbl", posInt, posDbl);
    CompareZeros.compare("posInt <-> negInt", posInt, negInt);

    CompareZeros.compare("posDbl <-> negDbl", posDbl, negDbl);
    CompareZeros.compare("posDbl <-> posInt", posDbl, posInt);
    CompareZeros.compare("posDbl <-> negInt", posDbl, negInt);

    CompareZeros.compare("negInt <-> negDbl", negInt, negDbl);
    CompareZeros.compare("negInt <-> posInt", negInt, posInt);
    CompareZeros.compare("negInt <-> posDbl", negInt, posDbl);

}

static void compare(final String id, final double arg0, final double arg1) {

    System.out.print(id + ": ");

    if (arg0 == arg1) {
        if (Double.compare(arg0, arg1) == 0) {
            System.out.println("OK");
        } else {
            System.out.println("Strange, and must be wrong!");
        }
    } else {
        if (Double.compare(arg0, arg1) == 0) {
            System.out.println("Strange, but perhaps logically ok");
        } else {
            System.out.println("Consistent...");
        }
    }

}
}

输出:

negDbl <-> posInt: Strange, and must be wrong!
negDbl <-> posDbl: Strange, and must be wrong!
negDbl <-> negInt: Strange, and must be wrong!
posInt <-> negDbl: Strange, and must be wrong!
posInt <-> posDbl: OK
posInt <-> negInt: OK
posDbl <-> negDbl: Strange, and must be wrong!
posDbl <-> posInt: OK
posDbl <-> negInt: OK
negInt <-> negDbl: Strange, and must be wrong!
negInt <-> posInt: OK
negInt <-> posDbl: OK

2 个答案:

答案 0 :(得分:7)

我检查了JDK中Double.compare的来源。它看起来像这样:

public static int compare(double d1, double d2) {
    if (d1 < d2)
        return -1;           // Neither val is NaN, thisVal is smaller
    if (d1 > d2)
        return 1;            // Neither val is NaN, thisVal is larger

    // Cannot use doubleToRawLongBits because of possibility of NaNs.
    long thisBits    = Double.doubleToLongBits(d1);
    long anotherBits = Double.doubleToLongBits(d2);

    return (thisBits == anotherBits ?  0 : // Values are equal
            (thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
             1));                          // (0.0, -0.0) or (NaN, !NaN)
}

最后几行解释了为什么会发生这种情况。这两个参数被转换为长位。然后比较这些位。 -0.00.0在位中的表示方式不同。 0.0的长值大于-0.0。前者为0,后者为-9223372036854775808。

docs也这样说:

  该方法认为

0.0d大于-0.0d。

为什么-0会有所不同?

这是因为-0是应用于整数文字0的否定运算符,其计算结果为0。然后0会隐式转换为double

至于为什么==认为负零和正零相同,在JLS中明确指出 - section 15.21.1

  

15.21.1。数值等式算子==和!=

     

...

     

根据IEEE 754标准的规则执行浮点相等测试:

     

...

     
      
  • 正零和负零被认为是相等的。
  •   

答案 1 :(得分:1)

记录

Double.compare等同于

  

public static int compare(double d1,                             双d2)

     

比较两个指定的double值。返回的整数值的符号与调用返回的整数的符号相同:

   new Double(d1).compareTo(new Double(d2))

接口Comparable指定的compareTo合同要求此方法强加总订单:

  

此接口对实现它的每个类的对象强加一个总排序。这种排序被称为类的自然排序,而类的compareTo方法被称为其自然比较方法。

因此,它无法实现IEEE 754定义并由numeric comparison operators实现的比较关系:

  

根据IEEE 754标准规范确定的浮点比较结果如下:

     
      
  • 如果任一操作数为NaN,则结果为false。

  •   
  • NaN以外的所有值都是有序的,负无穷大小于所有有限值,正无穷大大于所有有限值。

  •   
  • 正零和负零被视为相等。

  •   

......这显然不是一个完整的订单。

总顺序是稳定排序的先决条件之一,当然也是可比类型的理想特征。