我在比较双零时遇到了一些奇怪的事情。根据双零基元的启动方式,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
答案 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.0
和0.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)
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以外的所有值都是有序的,负无穷大小于所有有限值,正无穷大大于所有有限值。
正零和负零被视为相等。
......这显然不是一个完整的订单。
总顺序是稳定排序的先决条件之一,当然也是可比类型的理想特征。