博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
180706-BigDecimal除法的精度问题
阅读量:6594 次
发布时间:2019-06-24

本文共 2527 字,大约阅读时间需要 8 分钟。

logo

BigDecimal除法的精度问题

在使用BigDecimal的除法时,遇到一个鬼畜的问题,本以为的精度计算,结果使用返回0,当然最终发现还是自己的使用姿势不对导致的,因此记录一下,避免后面重蹈覆辙

<!-- more -->

I. 问题抛出

在使用BigDecimal做高精度的除法时,一不注意遇到了一个小问题,如下

@Testpublic void testBigDecimal() {    BigDecimal origin = new BigDecimal(541253);    BigDecimal now = new BigDecimal(12389431);    BigDecimal val = origin.divide(now, RoundingMode.HALF_UP);    System.out.println(val);    origin = new BigDecimal(541253);    now = new BigDecimal(12389431.3);    val = origin.divide(now, RoundingMode.HALF_UP);    System.out.println(val);    origin = new BigDecimal(541253.4);    now = new BigDecimal(12389431);    val = origin.divide(now, RoundingMode.HALF_UP);    System.out.println(val);}

上面的输出是什么 ?

000.043686703610520937021487456961257

为什么前面两个会是0呢,如果直接是 541253 / 12389431 = 0 倒是可以理解, 但是BigDecimal不是高精度的计算么,讲道理不应该不会出现这种整除的问题吧

我们知道在BigDecimal做触发时,可以指定保留小数的参数,如果加上这个,是否会不一样呢?

BigDecimal origin = new BigDecimal(541253);BigDecimal now = new BigDecimal(12389431);BigDecimal val = origin.divide(now, 5, RoundingMode.HALF_UP);System.out.println(val);

输出结果为:

0.04369

所以说在指定了保留小数之后,则没有问题,所以大胆的猜测一下,是不是上面的几种case中,由于scale值没有指定时,默认值不一样,从而导致最终结果的精度不同呢?

简单的深入源码分析一下,执行的方式为 origin.divide(now, RoundingMode.HALF_UP);, 所以这个scale参数就瞄准origin对象,而这个对象,就只能去分析它的构造了,因为没有其他的地方使用

II. 源码定位

1. 整形传参构造

分析下面这一行, 直接进入源码

BigDecimal origin = new BigDecimal(541253);

很明显的int传参构造,进去简单看一下

// java.math.BigDecimal#BigDecimal(int)public BigDecimal(int val) {    this.intCompact = val;    this.scale = 0;    this.intVal = null;}public BigDecimal(long val) {    this.intCompact = val;    this.intVal = (val == INFLATED) ? INFLATED_BIGINT : null;    this.scale = 0;}

so,很明确的知道默认的scale为0,也就是说当origin为正数时,以它进行的除法,不现实指定scale参数时,最终返回的都是没有小数的,同样看一眼,还有long的传参方式, BigInteger也一样

2. 浮点传参

接下来就是浮点的scale默认值确认了,这个构造相比前面的复杂一点,源码就不贴了,太长,也看不太懂做了些啥,直接用猥琐一点的方式,进入debug模式,单步执行

@Testpublic void testBigDecimal() {    BigDecimal origin = new BigDecimal(541253.0);    BigDecimal now = new BigDecimal(12389431.1);    BigDecimal tmp = new BigDecimal(0.0);}

根据debug的结果,第一个,scale为0; 第二个scale为29, 第三个scale为0

origin

now

tmp

3. String传参

依然是一大串的逻辑,同样采用单步debug的方式试下

@Testpublic void testBigDecimal() {    BigDecimal origin = new BigDecimal("541253.0");    BigDecimal now = new BigDecimal("12389431.1");    BigDecimal t = new BigDecimal("0.0");}

上面三个的scale都是1

smaple

4. 小结

  • 对于BigDecimal进行除法运算时,最好指定其scale参数,不然可能会有坑
  • 对于BigDecimla的scale初始化的原理,有待深入看下BigDecimal是怎么实现的

最后贴一张乘法的图作为收尾

mul

II. 其他

1. :

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

  • 微博地址:
  • QQ: 一灰灰/3302797840

3. 扫描关注

QrCode

转载地址:http://wqcio.baihongyu.com/

你可能感兴趣的文章
SQL SERVER 备份数据库到指定路径语句
查看>>
3.Knockout.Js(属性绑定)
查看>>
v140平台工具集与v110工具集选择
查看>>
EF6+Sqlite连接字符串的动态设置
查看>>
下拉加载更多
查看>>
您是哪一种类型的老板?
查看>>
SQL SERVER 2012 只能识别20个CPU的问题
查看>>
设计模式(十)外观模式
查看>>
C/C++语言中Static的作用详述
查看>>
[Android Samples视频系列之ApiDemos] App-Activity-Recreate
查看>>
ASP开发基础
查看>>
MYSQL性能调优
查看>>
LVM自动扩容
查看>>
笔记整理4
查看>>
idea文件折叠显示出来配置
查看>>
SQLSERVER中的非工作时间不得插入数据的触发器的实现
查看>>
如何写出兼容大部分浏览器的CSS 代码
查看>>
第二阶段冲刺第八天,6月7日。
查看>>
java的左移位(<<)和右移位(>>)和无符号右移(>>>)
查看>>
struts2 action 返回类型分析
查看>>