【问题标题】:Does Java Float.compare() Always Produce The Correct Result?Java Float.compare() 是否总是产生正确的结果?
【发布时间】:2019-11-15 14:55:31
【问题描述】:

这似乎是一个我可以轻松找到答案的问题,但我看不到任何条目。我知道浮点运算是如何工作的,为了比较浮点数,我需要使用 epsilon 检查。当我与我的团队分享这个问题时,我的一位同事问我这个问题,我无法回答。

Java 上的 compare 方法是否总是产生正确的结果,即对 f1f2 进行 epsilon 检查会产生的结果?

Float.compare(float f1, float f2);

注意:特别考虑这个问题的平等情况。

【问题讨论】:

  • 您能告诉我们什么是 epsilon 检查吗?我试图搜索它,但我什么也没找到
  • 它没有。它为 f1 < f2 返回 -1,为 f1 > f2 返回 1。
  • @ControlAltDel 我认为他的意思是docs.microsoft.com/en-us/dotnet/api/…
  • 当您检查两个浮点数是否相等时,== 运算符可能不会因为浮点精度而给您正确的结果。相反,您想定义一个非常小的 epsilon 值(如 0.00001)并检查它们的绝对差是否小于 epsilon。
  • 不采用任何 epsilon 值的方法如何返回与 epsilon 检查相同的东西,根据定义,使用 epsilon,可能是 0.00001、0.1 或 2089? javadoc 告诉方法返回什么:docs.oracle.com/javase/8/docs/api/java/lang/…

标签: java floating-point


【解决方案1】:

不,Float.compare 不使用任何类型的 epsilon 检查。

以下是该方法的 OpenJDK 13 实现示例:

/**
 * Compares the two specified {@code float} values. The sign
 * of the integer value returned is the same as that of the
 * integer that would be returned by the call:
 * <pre>
 *    new Float(f1).compareTo(new Float(f2))
 * </pre>
 *
 * @param   f1        the first {@code float} to compare.
 * @param   f2        the second {@code float} to compare.
 * @return  the value {@code 0} if {@code f1} is
 *          numerically equal to {@code f2}; a value less than
 *          {@code 0} if {@code f1} is numerically less than
 *          {@code f2}; and a value greater than {@code 0}
 *          if {@code f1} is numerically greater than
 *          {@code f2}.
 * @since 1.4
 */
public static int compare(float f1, float f2) {
    if (f1 < f2)
        return -1;           // Neither val is NaN, thisVal is smaller
    if (f1 > f2)
        return 1;            // Neither val is NaN, thisVal is larger

    // Cannot use floatToRawIntBits because of possibility of NaNs.
    int thisBits    = Float.floatToIntBits(f1);
    int anotherBits = Float.floatToIntBits(f2);

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

Source

【讨论】:

  • 我实际上检查了这个,但我不确定它的位掩码是如何工作的。无论如何,谢谢你说清楚。
  • 像往常一样,Java 源代码针对速度而非可读性进行了高度优化。此外,应该始终遵循 javadoc 中的定义(或缺少它)而不是实现。如果您依赖Javadoc中的行为,实现可能会发生变化,并且可能会破坏您的代码。
  • @MarkJeronimus 是的,我完全同意。这个例子只是为了说明实现不使用它,所以它显然不能成为规范的一部分。
【解决方案2】:

如果您阅读 Float.compare() 的 Javadoc,他们会谈论“数值相等”。这意味着表示相同理论数字但编码不同的值被认为是相等的。例如0.0 == -0.0 和次正规数。

Epsilon 检查(检查两个数字是否在彼此的 范围 内,通常是一个非常小的数字),不是事实上的标准,并且有很多实际问题(比如当你不知道数字的大小时选择哪个 epsilon,或者当两个数字的大小差异很大时该怎么做)。由于这个原因,Java 只实现了精确的操作。

【讨论】:

    【解决方案3】:

    不,java.lang.Float.compare( double, double ) 不会为彼此隐藏的 epsilon 值内的值返回 0(相等)。

    但是,您可以轻松地手动进行此类比较,或者使用一些常用库之一。

    手动,您可以编写一个函数,如果相等的模糊检查通过,则返回零,否则返回 Float.compare()。相等性的模糊检查可以是 Math.abs( f1 - f2 ) &lt; epsilon

    另外,在 Apache Commons Math 中,Precision 类为接受 epsilon 值的浮点数和双精度数提供 compares() 和 equals() 方法。并且该类在常量 EPSILON 中为 epsilon 提供了一个值。

    或者,在 Google Guava 中,DoubleMath 类提供了接受双精度值和 epsilon 值的 blurCompare() 和 blurEquals() 方法。

    【讨论】:

      【解决方案4】:

      听起来您需要检查两个浮点值在给定的非负增量内是否相等,对吗?

      您可以使用 JUnit 的 Assertions.assertEquals() 方法来实现:

      assertEquals​(float f1, float f2, float delta)
      

      【讨论】:

      • 奇怪的建议。那是一个 test 库。你怎么能在生产代码中使用它?!
      • 我想我的问题很清楚,我想知道给定的方法是否可以。
      • @GhostCat 说 Reinstate Monica 你在哪里看到生产代码?
      • 您在哪里看到“仅测试”代码?
      【解决方案5】:

      通常使用如下测试完成:if (abs(a-b) &lt; DELTA) ... 其中 DELTA 被全局声明为一些非常小的数字常量,以保持整个程序的一致性。由于测试非常简单,我喜欢明确说明。

      【讨论】:

      • 我想我的问题很清楚,我想知道给定的方法是否可以。
      猜你喜欢
      • 2011-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-14
      • 2015-10-12
      • 2015-01-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多