金额计算使用BigDecimal精确度问题

BigDecimal精确度问题

很多涉及金额计算不能使用double、float等类型,而是使用精确度更高的bigdecimal;

所以,很多支付、电商、金融等业务中,BigDecimal的使用非常频繁。但是,如果误以为只要使用BigDecimal表示数字,结果就一定精确,那就大错特错了!

阿里巴巴手册中有一条禁止使用构造方法bigdecimal(double)方式把double值转化为bigdecimal对象
在这里插入图片描述

那到底应该如何正确的创建一个BigDecimal?
上图推荐了两种方式new bigdecimal(“0.1”)、bigdecimal.valueOf(0.1)
那么BigDecimal是如何做精确度计算的呢
如果大家看过BigDecimal的源码,其实可以发现,实际上一个BigDecimal是通过一个"无标度值"和一个"标度"来表示一个数的。

在BigDecimal中,标度是通过scale字段来表示的。

而无标度值的表示比较复杂。当unscaled value超过阈值(默认为Long.MAX_VALUE)时采用intVal字段存储unscaled value,intCompact字段存储Long.MIN_VALUE,否则对unscaled value进行压缩存储到long型的intCompact字段用于后续计算,intVal为空;
在这里插入图片描述
scale()返回scale标度,其中注释非常清楚;
如果scale为零或正值,则该值表示这个数字小数点右侧的位数。如果scale为负数,则该数字的真实值需要乘以10的该负数的绝对值的幂。例如,scale为-3,则这个数需要乘1000,即在末尾有3个0。
如123.123,那么如果使用BigDecimal表示,那么他的无标度值为123123,他的标度为3。
而二进制无法表示的0.1,使用BigDecimal就可以表示了,及通过无标度值1和标度1来表示。
创建bigdecimal对象有四种方法;
bigdecimal(int)
bigdecimal(double)
bigdecimal(long)
bigdecimal(string)
四种方式创建出的标度scale都是不一样的;
其中 BigDecimal(int)和BigDecimal(long) 比较简单,因为都是整数,所以他们的标度都是0;
而BigDecimal(double) 和BigDecimal(String)的标度就有很多学问了;
BigDecimal中提供了一个通过double创建BigDecimal的方法——BigDecimal(double) ,但是,同时也给我们留了一个坑!
因为我们知道,double表示的小数是不精确的,如0.1这个数字,double只能表示他的近似值。
所以,当我们使用new BigDecimal(0.1)创建一个BigDecimal 的时候,其实创建出来的值并不是正好等于0.1的;
而是0.1000000000000000055511151231257827021181583404541015625。这是因为doule自身表示的只是一个近似值;
在这里插入图片描述
上图可以看出bigdecimal(double)的精度是不精确的,double本身就是一个近似值;

在这里插入图片描述
而bigdecimal(string)是精确的值,它的标度是1;
但是需要注意的是,new BigDecimal(“0.10000”)和new BigDecimal(“0.1”)这两个数的标度分别是5和1,如果使用BigDecimal的equals方法比较,得到的结果是false,具体原因和解决办法参考为什么阿里巴巴禁止使用BigDecimal的equals方法做等值比较?
在这里插入图片描述
这里bigdecimal.valueOf(0.1)为什么也能精确呢,
使用了于double.tostring(),转化为第一种方式new bigdecimal(string)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oNuoyi

你的鼓励将是我创作的做大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值