newdivide歌词Java中BigDecimal除法使⽤不当导致精度问题
⽬录
I. 问题抛出
II. 源码定位
1. 整形传参构造
2. 浮点传参
3. String传参
4. ⼩结
在使⽤BigDecimal的除法时,遇到⼀个⿁畜的问题,本以为的精度计算,结果使⽤返回0,当然最终发现还是使⽤姿势不对导致的,因此记录⼀下,避免后⾯重蹈覆辙
I. 问题抛出
在使⽤BigDecimal做⾼精度的除法时,⼀不注意遇到了⼀个⼩问题,如下
@Test
public 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);
}
上⾯的输出是什么?
0.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模式,单步执⾏
@Test
public 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
3. String传参
依然是⼀⼤串的逻辑,同样采⽤单步debug的⽅式试下
@Test
public void testBigDecimal() {
BigDecimal origin = new BigDecimal("541253.0");
BigDecimal now = new BigDecimal("12389431.1");
BigDecimal t = new BigDecimal("0.0");
}
上⾯三个的scale都是1
4. ⼩结
对于BigDecimal进⾏除法运算时,最好指定其scale参数,不然可能会有坑
对于BigDecimla的scale初始化的原理,有待深⼊看下BigDecimal是怎么实现的
最后贴⼀张乘法的图作为收尾
到此这篇关于Java中BigDecimal除法使⽤不当导致精度问题的⽂章就介绍到这了,更多相关Java BigDecimal除法精度内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!