【问题标题】:Why is Java's Double.compare(double, double) implemented the way it is?为什么 Java 的 Double.compare(double, double) 是这样实现的?
【发布时间】:2009-11-12 23:55:45
【问题描述】:

我正在查看 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)
}

这个实现的优点是什么?


编辑:“优点”是一个(非常)糟糕的词选择。我想知道这是如何工作的。

【问题讨论】:

  • 与此相关以及此处的所有讨论:查看浮点比较领域的 JDK 可怕之处:publicobject.com/2009/11/floating-point-equality.html
  • @KevinBourrillion - 按照我的阅读方式,这实际上是关于 IEE 浮点标准的可怕之处。 Java 必须按照 IEE 标准运行,因为这是现代机器上的硬件实现的。

标签: java comparison floating-point


【解决方案1】:

解释在代码中的 cmets 中。 Java 对0.0-0.0 以及“不是数字”(NaN) 都有双重值。您不能对这些值使用简单的 == 运算符。查看doubleToLongBits() 源和the Javadoc for the Double.equals() method

请注意,在大多数情况下,对于两个 Doubled1d2 类的实例, d1.equals(d2) 的值为 true 如果 并且仅当

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

也有值true。然而, 有两个例外:

  • 如果d1d2都代表Double.NaN,那么equals方法返回true,甚至 虽然Double.NaN == Double.NaN 的值是false
  • 如果d1 代表+0.0d2 代表-0.0,或者反之亦然,即使+0.0 == -0.0 的值是true,相等测试的值也是false

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

【讨论】:

    【解决方案2】:

    @Shoover 的回答是正确的 (read it!),但还有更多内容。

    正如javadoc for Double::equals 所说:

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

    假设 Java 设计者决定在封装的 double 实例上实现 equals(...)compare(...),其语义与 == 相同。这意味着 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.
    }
    

    这样做没有多大意义!

    由于-0.0+0.0 具有不同的位模式但根据== 是相等的,因此会存在其他异常。

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

    【讨论】:

    • 谢谢,斯蒂芬。我将您的答案标记为正确而不是 shoover 的,因为我想知道为什么 Java 设计人员实现了比较他们所做的方式(即“优点”),而不仅仅是代码做了什么(尽管我也不知道)。我很抱歉没有更明确,但感谢你们俩。
    • 由于浮点精度问题,我认为使用它们作为查找键的代码只会出现问题。
    • @280228 - 是的,密钥 >>could
    • @SamHarwell:假设一个采用double 的函数计算起来有点昂贵;根据预期的唯一操作数值的数量,这样的函数可以使用Map&lt;Double,Double&gt;,以便对已经计算过的值的调用可以简单地返回早期计算的结果。
    【解决方案3】:

    优点是它是满足规范的最简单的代码。

    菜鸟程序员的一个共同特点是高估阅读源代码而低估阅读规范。在这种情况下,规范:

    http://java.sun.com/javase/6/docs/api/java/lang/Double.html#compareTo%28java.lang.Double%29

    ...使行为和行为的原因(与equals() 的一致性)非常清楚。

    【讨论】:

    • 嗯,你是对的,我应该仔细阅读,但你不应该认为我低估了规格。我从用于 compare(...) 的 Javadoc 开始,然后转到 doubleToLongBits(...),然后查看了 IEEE 754 上的 Wikipedia 文章,那时我已经忘记了之前提到的 compareTo(.. .)。所以,我认为你必须同意,高估源代码不是我的问题,但阅读规范效率低下才是! p.s.您的链接已损坏。
    【解决方案4】:

    该实现允许将实数定义为

    【讨论】:

      猜你喜欢
      • 2014-11-08
      • 1970-01-01
      • 2012-09-27
      • 1970-01-01
      • 2013-07-10
      • 1970-01-01
      • 1970-01-01
      • 2014-06-10
      • 1970-01-01
      相关资源
      最近更新 更多