BigDecimal精确度问题

导读:本篇文章讲解 BigDecimal精确度问题,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

BigDecimal精确度问题

来源于关注Hollis公众号,认为很有用在此记录和分享;

很多涉及金额计算不能使用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)
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/5302.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!