为什么Java的Double.compare(double,double)是这样实现的?

2022-09-01 03:35:44

我正在研究Java标准库中比较(double,double)的实现(6)。上面写着:

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

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

此实现的优点是什么?


编辑:“优点”是一个(非常)糟糕的词语选择。我想知道这是怎么回事。


答案 1

说明在代码的注释中。Java 对 和 都有双精度值,以及 “not a number” ()。不能对这些值使用简单运算符。看一下源代码和 Javadoc 的 Double.equals() 方法0.0-0.0NaN==doubleToLongBits()

请注意,在大多数情况下,对于类 和 的两个实例,的值是当且仅当Doubled1d2d1.equals(d2)true

d1.doubleValue() == d2.doubleValue()

也具有值 。但是,有两种例外情况:true

  • 如果 和 都表示,则 equals 方法返回 ,即使具有值 。d1d2Double.NaNtrueDouble.NaN == Double.NaNfalse
  • 如果表示 while 表示 ,反之亦然,则相等检验具有值 ,即使具有值 。d1+0.0d2-0.0false+0.0 == -0.0true

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


答案 2

@Shoover的答案是正确的(阅读它!),但还有更多的东西。

正如 javadoc 所述:Double::equals

“这个定义允许哈希表正常运行。

假设 Java 设计人员决定实现与包装实例相同的语义。这意味着它将始终返回包装的NaN。现在考虑一下,如果您尝试在地图或集合中使用包装的 NaN,会发生什么情况。equals(...)compare(...)==doubleequals()false

List<Double> l = new ArrayList<Double>();
l.add(Double.NaN);
if (l.contains(Double.NaN)) {
    // this wont be executed.
}

Map<Object,String> m = new HashMap<Object,String>();
m.put(Double.NaN, "Hi mum");
if (m.get(Double.NaN) != null) {
    // this wont be executed.
}

没有多大意义吧!

存在其他异常,因为 和 具有不同的位模式,但根据 相同。-0.0+0.0==

因此,Java设计人员决定(正确地IMO)为我们今天拥有的这些Double方法定义更复杂(但更直观)。


推荐