场景
阿里Java开发手册嵩山版中明确指出:
1、BigDecimal的等值比较应使用compareTo()方法,而不是equals()方法
equals()方法会比较值和精度(1.0与1.00返回结果为false),而compareTo()则会忽略精度
2、禁止使用构造方法BigDecimal(double)的方式把double值转换为BigDecimal对象
BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常
注:
博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主
Java开发手册为什么禁止使用BigDecimal的equals方法做等值比较?
BigDecimal,相信对于很多人来说都不陌生,很多人都知道他的用法,这是一种java.math 包中提供的一种可以用来
进行精确运算的类型。在进行金额表示、金额计算等场景,不能使用 double、float 等类型,而是要使用对精度支持
更好的 BigDecimal。其内部自带了很多方法,如加,减,乘,除等运算方法都是可以直接调用的。除了需要用
BigDecimal 表示数字和进行数字运算以外,代码中还经常需要对于数字进行相等判断。Java开发手册中有说明:
BigDecimal的等值比较应使用compareTo()方法,而不是equals()方法
equals()方法会比较值和精度(1.0与1.00返回结果为false),而compareTo()则会忽略精度。
看下面的示例:
BigDecimal bigDecimal = new BigDecimal(1);
BigDecimal bigDecimal1 = new BigDecimal(1);
System.out.println(bigDecimal.equals(bigDecimal1));//true
BigDecimal bigDecimal2 = new BigDecimal(1);
BigDecimal bigDecimal3 = new BigDecimal(1.0);
System.out.println(bigDecimal2.equals(bigDecimal3));//true
BigDecimal bigDecimal4 = new BigDecimal("1");
BigDecimal bigDecimal5 = new BigDecimal("1.0");
System.out.println(bigDecimal4.equals(bigDecimal5));//false
通过以上代码示例,我们发现,在使用 BigDecimal 的 equals 方法对 1 和 1.0 进行比较的时候,
有的时候是 true(当使用 int、double 定义 BigDecimal 时),有的时候是false(当使用 String 定义 BigDecimal 时)。
查看源码可知,equals会比较标度scale,见源码
BigDecimal 一共有以下 4 个构造方法:
BigDecimal(int) 因为是整数,所以标度就是 0
BigDecimal(double) 无论我们使用 new BigDecimal(0.1)还是 new BigDecimal(0.10)定义,他的近似值
都是0.1000000000000000055511151231257827021181583404541015625
这个,那么他的标度就是这个数字的位数,即 55
BigDecimal(long) 因为是整数,所以标度就是 0
BigDecimal(String) BigDecimal(“1”)和 BigDecimal(“1.0”)的标度不一样
BigDecimal 中提供了 compareTo 方法,这个方法就可以只比较两个数字的值,如果两个数相等,则返回 0。
BigDecimal bigDecimal6 = new BigDecimal("1");
BigDecimal bigDecimal7 = new BigDecimal("1.0000");
System.out.println(bigDecimal6.compareTo(bigDecimal7)); //0
Java开发手册为什么禁止使用double直接构造BigDecimal
Java开发手册中要求 禁止使用构造方法BigDecimal(double)的方式把double值转换为BigDecimal对象
说明:BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常
比如下面,实际存储值为0.10000000149
BigDecimal bigDecimal = new BigDecimal(0.1F);
优先推荐入参为String 的构造方法,或使用BigDecimal的valueOf方法,此方法其实内部执行了Double的toString,
而Double的toString按double的实际能表达的精度对尾数进行了截断。
BigDecimal bigDecimal1 = new BigDecimal("0.1");
BigDecimal bigDecimal2 = BigDecimal.valueOf(0.1);
Java中doble为什么不精确?
十进制小数转成二进制,一般采用”乘 2 取整,顺序排列”方法,如 0.625 转成二进制的表示为 0.101。
但是,并不是所有小数都能转成二进制,如 0.1 就不能直接用二进制表示,他的二进制是 0.000110011001100...
这是一个无限循环小数。所以,计算机是没办法用二进制精确的表示 0.1 的。也就是说,在计算机中,
很多小数没办法精确的使用二进制表示出来。在 Java 中,使用 float 和 double 分别用来表示单精度浮点数和双精度浮点数。
所谓精度不同,可以简单的理解为保留有效位数不同。采用保留有效位数的方式近似的表示小数。