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的结果也不同。

原文地址:https://www.cnblogs.com/filozofio/p/12304424.html

时间: 2024-08-29 19:35:10

Java浮点数相等性的判断的相关文章

java的跨平台性指的什么

java的跨平台,是指java运行时候凌驾于os之上,是在jvm中运行的,跟os没有直接联系. 如果有机会写数据导入导出,和服务器之间交互的应用,就会知道“跨平台”有什么意义了. 在java之前,跨平台是很痛苦的事情,主要是因为所有对于系统的调用,在不同的操作系统下结果都不一样,简单的来说,int的长度在不同操作系统里面就不一样,甚至于连字节排列的顺序都不同.调用同一个函数返回的格式也不同,因此,写程序的人自己要对不同的系统非常了解,如果真的需要对不同操作系统进行兼容,要在程序里面写很多if,编

C#与Java对比学习:类型判断、类与接口继承、代码规范与编码习惯、常量定义(转载)

C#与Java对比学习:类型判断.类与接口继承.代码规范与编码习惯.常量定义 类型判断符号: C#:object a;  if(a is int) { }  用 is 符号判断 Java:object a; if(a instanceof Integer) { } 用 instanceof 符号判断 类与接口的继承: C#:public class MDataRow : List<MDataCell>, IDataRecord, ICustomTypeDescriptor Java:publi

js,java,浮点数运算错误及应对方法

js,java,浮点数运算错误及应对方法 一,浮点数为什么会有运算错误 IEEE 754 标准规定了计算机程序设计环境中的二进制和十进制的浮点数自述的交换.算术格式以及方法. 现有存储介质都是2进制.2进制的进制基数是2,那么一个数字只要被因素包含大于2的质数的数除,都会产生无限循环小数.无限循环小数和无理数都无法,和非无限循环的有理数一起用同一种方式存储到存储介质上的同时还保持计算的兼容性. 对于无限循环小数,可以设计一种格式存储到介质上,但是同时又要和非无限循环的有理数能够计算,效率应该会变

Java杂谈之二----怎样判断一个数是水仙花数以及穷举水仙花数

首先明确一下什么是水仙花数 百度说,水仙花数指一个n位数(n>=3),它的每个位上的数字的n次幂之和等于它本身 例如:1^3+5^3+3^3=153 水仙花数只是自幂数的一种,严格来说三位数的3次幂数才能成为水仙花数. 但其实也分一位自幂数,两位自幂数,三位自幂数,四位自幂数等等. 所以鉴于水仙花数的定义的不确定和模糊性 以下代码示例不仅限于三位数的水仙花数,主要涉及的是思想问题. 类名:JavaNarcissus 构造函数:JavaNarcissus() 判断一个数是否为水仙花数:IsNarc

体验Java的封装性

1 package com.cnblogs.java; 2 //体验Java的封装性 3 /* 4 * 如下的人类年龄赋值-300岁,显然很不合理,这种直接对类的属性赋值,有时候虽然不合理但却会编译通过. 5 * 所以我们考虑不让对象直接操作属性,而是通过对象调用方法来对属性赋值,在方法中我们就可以进一步限制 6 * 赋值的大小问题. 7 * 解决办法:将类的属性私有化,通过共有的方法来调用修改类的属性.(在方法中限制修改的值) 8 */ 9 public class TestPerson {

java编写输入一个数判断是否是回文数,所谓回文数比如121,1221,6778776

package com.hao947; import java.util.Scanner; public class demo5 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int x = 0; x = scanner.nextInt(); System.out.println("请输入一个4-8位的数"); int dig[] = new int[10]; i

Java编程练习之判断Java文件名是否正确,判断邮箱格式是否正确和统计指定字符串中某字符现的次数

一判断Java文件名是否正确,判断邮箱格式是否正确 功能:判断Java文件名是否正确,判断邮箱格式是否正确.其中:合法的文件名应该以.java结尾:合法的邮箱名 中至少要包含 "@" , 并要求 "@" 在 "." 之前. 练习代码: public class Test { public static void main(String[] args) { //Java文件名 String fileName = "HelloWorld.j

JAVA 浮点数转化为百分数,分离整数和小数部分

JAVA 浮点数转化为百分数 public class DoubleToPercentformat { /** * 将double类型数据转换为百分比格式,并保留小数点前IntegerDigits位和小数点后FractionDigits位 * @param d * @param IntegerDigits * @param FractionDigits * @return */ public static String getPercentFormat(double d,int IntegerD

java打开文件夹(含判断操作系统工具类和解压缩工具类)

1.Runtime.getRuntime().exec("explorer D:\\Java"); 2.java.awt.Desktop.getDesktop().open(new File("D:\\Java")); 4.java.awt.Desktop.getDesktop().browse(...) 3. try { String[] cmd = new String[5]; cmd[0] = "cmd"; cmd[1] = "/