杰瑞科技汇

Java中如何判断两个double值是否相等?

在 Java 中判断两个 double 类型变量是否相等,不能直接使用 运算符,这是因为 double 是浮点数类型,在计算机中是以二进制形式存储的,存在精度问题,很多十进制小数无法精确地用二进制表示,导致在进行计算时可能会产生微小的误差。

Java中如何判断两个double值是否相等?-图1
(图片来源网络,侵删)

直接使用 判断,可能会因为这些微小的误差而返回 false,即使从数学上看这两个数是相等的。


错误的做法(不推荐)

double a = 0.1 + 0.2;
double b = 0.3;
// 这种判断很可能会返回 false
if (a == b) {
    System.out.println("a 等于 b");
} else {
    System.out.println("a 不等于 b"); // 这行很可能会被执行
}

输出:

a 不等于 b

原因: 12 在二进制中都是无限循环小数,在计算 1 + 0.2 时会产生精度损失,导致结果 a 并不精确等于 3


正确的判断方法

有几种可靠的方法可以判断两个 double 是否“足够接近”,从而可以被认为是相等的。

Java中如何判断两个double值是否相等?-图2
(图片来源网络,侵删)

使用误差范围(推荐)

这是最常用、最直观的方法,我们定义一个很小的“误差范围”(epsilon),如果两个数的差值在这个范围内,就认为它们是相等的。

核心思想: if (Math.abs(a - b) < epsilon)

如何选择 epsilon 的值?

  • 对于一般的业务逻辑,1e-10 (0.0000000001) 是一个比较安全的选择。
  • 如果数值非常大,可以考虑使用相对误差(见方法二)。
  • 如果数值非常小,需要相应地调整 epsilon

示例代码:

Java中如何判断两个double值是否相等?-图3
(图片来源网络,侵删)
public class DoubleComparison {
    public static void main(String[] args) {
        double a = 0.1 + 0.2;
        double b = 0.3;
        // 定义一个很小的误差范围
        double epsilon = 1e-10;
        // 判断两个数的差值是否在误差范围内
        if (Math.abs(a - b) < epsilon) {
            System.out.println("a 和 b 在误差范围内相等。"); // 这行会被执行
        } else {
            System.out.println("a 和 b 不相等。");
        }
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("a - b = " + (a - b));
    }
}

输出:

a 和 b 在误差范围内相等。
a = 0.30000000000000004
b = 0.3
a - b = 4.440892098500626E-16

可以看到,ab 的实际差值非常小,远小于我们定义的 epsilon (1e-10),所以判断为相等。

使用 Apache Commons Lang 或 Guava 库

在实际项目中,为了避免重复造轮子和处理各种边界情况,强烈推荐使用成熟的第三方库。

使用 Apache Commons Lang

在你的项目中添加依赖(Maven):

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version> <!-- 请使用最新版本 -->
</dependency>

使用 DoubleUtils 类:

import org.apache.commons.lang3.math.DoubleUtils;
public class DoubleComparisonApache {
    public static void main(String[] args) {
        double a = 0.1 + 0.2;
        double b = 0.3;
        // DoubleUtils.equals 方法内部也使用了误差范围判断
        // 默认的 epsilon 是 1E-9
        if (DoubleUtils.equals(a, b)) {
            System.out.println("a 和 b 使用 Apache Commons Lang 判断为相等。"); // 这行会被执行
        } else {
            System.out.println("a 和 b 使用 Apache Commons Lang 判断为不相等。");
        }
    }
}

使用 Google Guava

添加依赖(Maven):

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.2-jre</version> <!-- 请使用最新版本 -->
</dependency>

使用 DoubleMath 类:

import com.google.common.math.DoubleMath;
public class DoubleComparisonGuava {
    public static void main(String[] args) {
        double a = 0.1 + 0.2;
        double b = 0.3;
        // DoubleMath.fuzzyEquals 方法提供了非常灵活的误差范围控制
        // 参数1: 第一个数, 参数2: 第二个数, 参数3: 误差范围
        if (DoubleMath.fuzzyEquals(a, b, 1e-10)) {
            System.out.println("a 和 b 使用 Guava 判断为相等。"); // 这行会被执行
        } else {
            System.out.println("a 和 b 使用 Guava 判断为不相等。");
        }
    }
}

使用 BigDecimal(适用于金融、财务等高精度场景)

如果对精度要求极高(如货币计算),最好的办法是使用 BigDecimal 类,它以十进制的形式存储数字,可以完美避免二进制浮点数带来的精度问题。

注意: 一定要用 String 构造 BigDecimal,而不是用 double,否则还是会把有问题的 double 值传进去。

import java.math.BigDecimal;
public class DoubleComparisonBigDecimal {
    public static void main(String[] args) {
        // 错误的构造方式,仍然会有精度问题
        // BigDecimal wrongA = new BigDecimal(0.1 + 0.2);
        // 正确的构造方式
        BigDecimal a = new BigDecimal("0.1").add(new BigDecimal("0.2"));
        BigDecimal b = new BigDecimal("0.3");
        // 使用 compareTo() 方法判断
        // compareTo() 返回 0 表示相等,返回 -1 表示小于,返回 1 表示大于
        if (a.compareTo(b) == 0) {
            System.out.println("a 和 b 使用 BigDecimal 判断为相等。"); // 这行会被执行
        } else {
            System.out.println("a 和 b 使用 BigDecimal 判断为不相等。");
        }
    }
}

总结与最佳实践

方法 优点 缺点 适用场景
直接 简单 错误,有精度问题 绝对不要用于判断 double 相等
误差范围法 简单,直观,不引入外部依赖 需要自己选择合适的 epsilon,对于极大或极小的数可能不适用 通用场景,快速实现,学习理解
Apache Commons / Guava 专业、可靠,代码简洁,处理了各种边界情况 需要引入第三方库 推荐用于所有生产项目
BigDecimal 精度最高,完美解决十进制问题 代码稍显繁琐,性能比 double 稍差 金融、财务、科学计算等对精度要求极高的场景
  • 如果你在学习或写简单的、非关键性的代码,使用 误差范围法
  • 如果你在开发正式的商业项目,强烈建议使用 Apache Commons LangGuava 库。
  • 如果你的项目涉及金钱、汇率等高精度计算,必须使用 BigDecimal
分享:
扫描分享到社交APP
上一篇
下一篇