Java中BigDecimal除法使用不当导致精度问题

 更新时间:2021年11月02日 15:39:45   作者:一灰灰  
本文主要介绍了Java中BigDecimal除法使用不当导致精度问题,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

在使用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
0
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除法精度内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot中的事务处理问题

    SpringBoot中的事务处理问题

    这篇文章主要介绍了SpringBoot中的事务处理问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • 深入了解Java核心类库--Arrays类

    深入了解Java核心类库--Arrays类

    这篇文章主要为大家详细介绍了java Arrays类定义与使用的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能给你带来帮助
    2021-07-07
  • Java实现企业发放的奖金根据利润提成问题

    Java实现企业发放的奖金根据利润提成问题

    这篇文章主要介绍了请利用数轴来分界,定位。注意定义时需把奖金定义成长整型,需要的朋友可以参考下
    2017-02-02
  • SpringCloud对服务内某个client进行单独配置的操作步骤

    SpringCloud对服务内某个client进行单独配置的操作步骤

    我们的微服务项目用的是springCloud,某个微服务接口因为数据处理量大,出现了接口超时的情况,我们需要单独修改这一个feignClient的超时时间,所以本文介绍了SpringCloud对服务内某个client进行单独配置的操作步骤,需要的朋友可以参考下
    2023-10-10
  • 使用Spring-Retry解决Spring Boot应用程序中的重试问题

    使用Spring-Retry解决Spring Boot应用程序中的重试问题

    重试的使用场景比较多,比如调用远程服务时,由于网络或者服务端响应慢导致调用超时,此时可以多重试几次。用定时任务也可以实现重试的效果,但比较麻烦,用Spring Retry的话一个注解搞定所有,感兴趣的可以了解一下
    2023-04-04
  • Elasticsearch查询之Match Query示例详解

    Elasticsearch查询之Match Query示例详解

    这篇文章主要为大家介绍了Elasticsearch查询之Match查询示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • JavaWeb中web.xml初始化加载顺序详解

    JavaWeb中web.xml初始化加载顺序详解

    本篇文章主要介绍了JavaWeb中web.xml初始化加载顺序详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • Java基于Socket实现网络编程实例详解

    Java基于Socket实现网络编程实例详解

    本文主要给大家介绍的是Java基于Socket实现网络编程的实例,并给大家介绍了TCP与UDP传输协议,有需要的小伙伴可以来参考下
    2016-07-07
  • Sleuth+logback 设置traceid 及自定义信息方式

    Sleuth+logback 设置traceid 及自定义信息方式

    这篇文章主要介绍了Sleuth+logback 设置traceid 及自定义信息方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • 如何解决HttpServletRequest.getInputStream()多次读取问题

    如何解决HttpServletRequest.getInputStream()多次读取问题

    这篇文章主要介绍了如何解决HttpServletRequest.getInputStream()多次读取问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07

最新评论