Java浮点数相等性的判断

Posted filozofio

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java浮点数相等性的判断相关的知识,希望对你有一定的参考价值。

Java浮点数相等性的判断

问题描述如下:
给定两个变量double a、double b,它们对应的装箱类型变量分别是Double x、Double y,问:

  1. 是否存在一组a、b、x、y,满足 a==b && !x.equals(y) ?
  2. 是否存在一组a、b、x、y,满足 a!=b && x.equals(y) ?

乍看之下似乎是不可能的,实际上确实存在这样的值,参考以下代码

public static void main() {
    double a = 0.0;
    double b = -0.0;
    double c = Double.NaN;
    double d = Double.NaN;
    Double x = a;
    Double y = b;
    Double z = c;
    Double w = d;
    System.out.println(a == b);         //输出true
    System.out.println(x.equals(y));    //输出false
    System.out.println(c == d);         //输出false
    System.out.println(w.equals(z));    //输出true
}

Double类型equals方法的实现和 ==操作符逻辑有所不同。
先看==操作符,以Java8为例,根据Java语言规范15.21.1,对于浮点数相等性判断,遵从IEEE 754规范:

  1. 只要有一个操作数是NaN==表达式的结果总是false,!=表达式的结果总是true。实际上,当且仅当x的值为NaN时,表达式 x!=x 为真。可以使用Float.NaN方法或者Double.NaN方法判断一个值是否是NaN
  2. 正数0与负数0相等,例如表达式 0.0==-0.0 为真。
  3. 除此之外,两个不同的浮点数使用==!=操作符判断相等性时,会认为它们不相等。尤其是,一个值表示正无穷,一个值表示负无穷;如果它们与自身比较,是相等的;与其他值比较,是不相等的。

再看JDK中Double的equals方法实现:

    public boolean equals(Object obj) {
        return (obj instanceof Double)
               && (doubleToLongBits(((Double)obj).value) ==
                      doubleToLongBits(value));
    }

    /**
     * Returns a representation of the specified floating-point value
     * according to the IEEE 754 floating-point "double
     * format" bit layout.
     *
     * <p>Bit 63 (the bit that is selected by the mask
     * {@code 0x8000000000000000L}) represents the sign of the
     * floating-point number. Bits
     * 62-52 (the bits that are selected by the mask
     * {@code 0x7ff0000000000000L}) represent the exponent. Bits 51-0
     * (the bits that are selected by the mask
     * {@code 0x000fffffffffffffL}) represent the significand
     * (sometimes called the mantissa) of the floating-point number.
     *
     * <p>If the argument is positive infinity, the result is
     * {@code 0x7ff0000000000000L}.
     *
     * <p>If the argument is negative infinity, the result is
     * {@code 0xfff0000000000000L}.
     *
     * <p>If the argument is NaN, the result is
     * {@code 0x7ff8000000000000L}.
     *
     * <p>In all cases, the result is a {@code long} integer that, when
     * given to the {@link #longBitsToDouble(long)} method, will produce a
     * floating-point value the same as the argument to
     * {@code doubleToLongBits} (except all NaN values are
     * collapsed to a single "canonical" NaN value).
     *
     * @param   value   a {@code double} precision floating-point number.
     * @return the bits that represent the floating-point number.
     */
    public static long doubleToLongBits(double value) {
        long result = doubleToRawLongBits(value);
        // Check for NaN based on values of bit fields, maximum
        // exponent and nonzero significand.
        if ( ((result & DoubleConsts.EXP_BIT_MASK) ==
              DoubleConsts.EXP_BIT_MASK) &&
             (result & DoubleConsts.SIGNIF_BIT_MASK) != 0L)
            result = 0x7ff8000000000000L;
        return result;
    }

Double的equals方法,通过==操作符,判断两个对象的doubleToLongBits返回值是否相等,来觉得两个对象是否相等。方法注释中有一句

If the argument is NaN, the result is 0x7ff8000000000000L

所以使用equals判断两个NaN时,结果为真。
对于其他情况,根据value的二进制表示,doubleToLongBits返回对应的long值。而0.0-0.0的符号位不同,所以二进制表示也不同,doubleToLongBits的结果也不同。

以上是关于Java浮点数相等性的判断的主要内容,如果未能解决你的问题,请参考以下文章

1203 判断浮点数是否相等

golang比较浮点数是不是相等

判断浮点数是否相等

如和判断两个浮点数是否相等

浮点数比较

浮点数比较