BigDecimal除法的精度问题

BigDecimal除法的精度问题BigDecimal除法的精度问题在使用BigDecimal的除法时,遇到一个鬼畜的问题,本以为的精度计算,结果使用返回0,当然最终发现还是自己的使用姿势不对导致的,因此记录一下,避免后面重蹈覆辙I.问题抛出在使用BigDecimal做高精度的除法时,一不注意遇到了一个小问题,如下@TestpublicvoidtestBigDecimal(){BigDecimal…

大家好,又见面了,我是你们的朋友全栈君。

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是怎么实现的

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

II. 其他

1. 一灰灰Blog: https://liuyueyi.github.io/hexblog

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

2. 声明

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

3. 扫描关注

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

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/149837.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • SQUID之refresh_pattern使用方法

    SQUID之refresh_pattern使用方法

    2021年7月24日
  • openssl安装教程(windows7系统,超详细)

    openssl安装教程1.安装包安装1.1所需资源链接1.2安装流程1.3测试是否安装成功1.4安装过程中的问题2.自己编译源码再安装1.安装包安装1.1所需资源链接openssl安装包下载地址:http://slproweb.com/products/Win32OpenSSL.html如果用谷歌浏览器打开的话,可以翻译成中文:对应英文如下:根据自己电脑的配置选择需要的版本,我这里选择的第1个Win64OpenSSLv1.1.1iLight。1.2安装流程下载好之后,直接双击即

    2022年4月11日
  • 如何运行SpringBoot项目

    如何运行SpringBoot项目最近在Ecplise上面写了一个简单的SpringBoot的测试项目,SpringBoot里面是有主函数的:我们知道的是在Ecplise上面找到这个主函数然后runas->javaApplication就可以了但是总不能一直不脱离Ecplise,总要出来自己单练的第一步:我就新建的一个文件夹boottest,然后右键导出整个工程:导出的是jar包,然后我们看…

    2022年10月13日
  • lunix针对用户的常用操作命令

    lunix针对用户的常用操作命令(1)使用who查看目前有哪些用户登录了服务器,见下图[root@vm18~]#whorootpts/02015-03-2710:23(192.168.9.188)从上文可以看出用户root使用ip地址为192.168.9.188登录到linux系统上 (2)看看root都在什么时间登录过系统 [root@vm18~]#lastrootrootpts/0192.168.9.1…

    2022年10月3日
  • django插件大全_裸缸用什么过滤器

    django插件大全_裸缸用什么过滤器前言如果需要满足前端各种筛选条件查询,我们使用drf自带的会比较麻烦,比如查询书名中包含“国”字,日期大于“2020-1-1”等等诸如此类的请求,Django-filter这个组件就是要解决这样的问

    2022年7月29日
  • HTML在线转换JS「建议收藏」

    HTML在线转换JS「建议收藏」HTML在线转换JS工具,它可以在线将html代码转换成js代码,您只需要将需要转换的代码放置于代码框内,然后点击对应的转换按钮。简单、实用、方便、快捷、无干扰。

    2022年9月8日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号