Java浮点数相等性的判断
问题描述如下:
给定两个变量double a、double b,它们对应的装箱类型变量分别是Double x、Double y,问:
- 是否存在一组a、b、x、y,满足 a==b && !x.equals(y) ?
- 是否存在一组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规范:
- 只要有一个操作数是
NaN
,==
表达式的结果总是false,!=
表达式的结果总是true。实际上,当且仅当x
的值为NaN
时,表达式x!=x
为真。可以使用Float.NaN
方法或者Double.NaN
方法判断一个值是否是NaN
。 - 正数0与负数0相等,例如表达式
0.0==-0.0
为真。 - 除此之外,两个不同的浮点数使用
==
或!=
操作符判断相等性时,会认为它们不相等。尤其是,一个值表示正无穷,一个值表示负无穷;如果它们与自身比较,是相等的;与其他值比较,是不相等的。
再看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
的结果也不同。
来源:https://www.cnblogs.com/filozofio/p/12304424.html