为什么Java的Double.compare(double,double)实现方式如何呢?

Dav*_*idS 35 java floating-point comparison

我正在研究Java标准库(6)中compare(double,double)的实现.它写道:

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)
}
Run Code Online (Sandbox Code Playgroud)

这个实现的优点是什么?


编辑:"优点"是一个(非常)糟糕的选择.我想知道它是如何工作的.

sho*_*ver 43

解释在代码中的注释中.Java有两个双值0.0-0.0,以及"不是一个数字"( NaN).您不能==对这些值使用简单运算符.看一下doubleToLongBits()源代码和方法的JavadocDouble.equals():

请注意,在大多数情况下,类的两个实例Double,d1并且d2,该值d1.equals(d2)true当且仅当

d1.doubleValue() == d2.doubleValue()
Run Code Online (Sandbox Code Playgroud)

也有价值true.但是,有两个例外:

  • 如果d1d2两者都表示Double.NaN,那么true即使Double.NaN == Double.NaN有值,equals方法也会返回false.
  • 如果d1代表+0.0while d2表示-0.0,反之亦然,则等值测试具有该值false,即使+0.0 == -0.0具有该值true.

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


Ste*_*n C 41

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

由于javadoc中Double::equals规定:

"这个定义允许哈希表正常运行."

假设Java设计者已决定实现equals(...)compare(...)使用与==包装double实例相同的语义.这意味着equals()将始终返回false包裹的NaN.现在考虑如果您尝试在Map或Collection中使用包装的NaN会发生什么.

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.
}
Run Code Online (Sandbox Code Playgroud)

这样做没有多大意义!

其他异常将存在,因为-0.0并且+0.0具有不同的位模式但是根据相同而不相同==.

因此,Java设计者(正确的IMO)决定了我们今天使用的这些Double方法的更复杂(但更直观)的定义.

  • @SamHarwell:假设一个带有"double"的函数计算起来有些昂贵; 根据预期的唯一操作数值的多少,这样的函数可以使用`Map <Double,Double>`,以便使用已经计算过的值的调用可以简单地返回先前计算的结果. (3认同)