BigDecimal避免丢失精度
通常我们会把一些小数类型的字段(比如:金额),定义成 BigDecimal
,而不是 Double
,避免丢失精度问题。
使用 Double 时可能会有这种场景:
1 | double amount1 = 0.02; |
正常情况下预计 amount2 - amount1 应该等于 0.01
但是执行结果,却为:
1 | 0.009999999999999998 |
实际结果小于预计结果。
Double 类型的两个参数相减会转换成二进制,因为 Double 有效位数为 16 位这就会出现存储小数位数不够的情况,这种情况下就会出现误差。
常识告诉我们使用 BigDecimal
能避免丢失精度。
但是使用 BigDecimal 能避免丢失精度吗?
答案是否定的。
为什么?
1 | BigDecimal amount1 = new BigDecimal(0.02); |
这个例子中定义了两个 BigDecimal 类型参数,使用构造函数初始化数据,然后打印两个参数相减后的值。
结果:
1 | 0.0099999999999999984734433411404097569175064563751220703125 |
不科学呀,为啥还是丢失精度了?
Jdk
中 BigDecimal
的构造方法
上有这样一段描述:
大致的意思是此构造函数的结果可能不可预测,可能会出现创建时为 0.1,但实际是 0.1000000000000000055511151231257827021181583404541015625 的情况。
由此可见,使用 BigDecimal 构造函数初始化对象,也会丢失精度。
那么,如何才能不丢失精度呢?
1 | BigDecimal amount1 = new BigDecimal(Double.toString(0.02)); |
我们可以使用 Double.toString
方法,对 double 类型的小数进行转换,这样能保证精度不丢失。
推荐
其实,还有更好的办法:
1 | BigDecimal amount1 = BigDecimal.valueOf(0.02); |
使用 BigDecimal.valueOf
方法初始化 BigDecimal 类型参数,也能保证精度不丢失。在新版的阿里巴巴开发手册中,也推荐使用这种方式创建 BigDecimal 参数。