BigDecimal注意事项

本文最后更新于 2024年6月18日 下午

BigDecimal注意事项

由于计算机是二进制的,在进行浮点数的存储和运算时,会出现精度丢失的风险,因此《阿里巴巴 Java 开发手册》中提到:“为了避免精度丢失,可以使用 BigDecimal 来进行浮点数的运算”。但是在使用 BigDecimal 时也会出现一些问题,下面就是一些常见的。

1. 精度丢失风险

首先我们要知道的是 BigDecimal 创建对象一般推荐使用的是静态方法 BigDecimal.valueOf(double) 和构造函数 BigDecimal(String)

《阿里巴巴 Java 开发手册》对这部分内容也有提到,如下图所示:

阿里巴巴 Java 开发手册

对于 BigDecimal 的运算方面,add 方法用于将两个 BigDecimal 对象相加,subtract 方法用于将两个 BigDecimal 对象相减。multiply 方法用于将两个 BigDecimal 对象相乘,divide 方法用于将两个 BigDecimal 对象相除。

1
2
3
4
5
6
7
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
System.out.println(a.add(b));// 1.9
System.out.println(a.subtract(b));// 0.1
System.out.println(a.multiply(b));// 0.90
System.out.println(a.divide(b));// 无法除尽,抛出 ArithmeticException 异常
System.out.println(a.divide(b, 2, RoundingMode.HALF_UP));// 1.11

这里需要注意的是,在我们使用 divide 方法的时候尽量使用 3 个参数版本,并且RoundingMode 不要选择 UNNECESSARY,否则很可能会遇到 ArithmeticException(无法除尽出现无限循环小数的时候),其中 scale 表示要保留几位小数,roundingMode 代表保留规则。

1
2
3
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) {
return divide(divisor, scale, roundingMode.oldMode);
}

2. 等值判断问题

《阿里巴巴 Java 开发手册》中提到:浮点数之间的等值判断,基本数据类型不能用 == 来比较,包装数据类型不能用 equals 来判断。因为浮点数采用 “尾数+阶码” 的编码方式,类似于科学计数法的 “有效数字+指数” 的表示方式,所以二进制无法精确表示大部分的十进制小数。

那么使用 BigDecimal 就能一劳永逸的用 equals() 来比较吗?

答案是不建议使用 equals() 来比较 BigDecimal 这是因为 equals() 方法不仅仅会比较值的大小(value)还会比较精度(scale),而 compareTo() 方法比较的时候会忽略精度。

《阿里巴巴 Java 开发手册》中提到:

阿里巴巴 Java 开发手册

compareTo() 方法可以比较两个 BigDecimal 的值,如果相等就返回 0,如果第 1 个数比第 2 个数大则返回 1,反之返回-1。

3. 设置保留几位小数

通过 setScale方法设置保留几位小数以及保留规则。保留规则有挺多种,不需要记,IDEA 会提示。

1
2
3
BigDecimal m = new BigDecimal("1.255433");
BigDecimal n = m.setScale(3,RoundingMode.HALF_DOWN);
System.out.println(n);// 1.255

参考文章

BigDecimal 详解


BigDecimal注意事项
http://cloudyw.cn/2024/06/18/BigDecimal注意事项/
作者
cloudyW
发布于
2024年6月18日
许可协议