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. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

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

2. 声明

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

3. 扫描关注

180706-BigDecimal除法的精度问题的更多相关文章

  1. BigDecimal 小数 浮点数 精度 财务计算

    简介 float和double类型的使用局限: 单精度浮点型变量float可以处理6~7位有效数,双精度浮点型变量double可以处理15~16位有效数,在实际应用中,如果需要对更大或者更小的数进行运 ...

  2. BigDecimal 解决double精度丢失问题(加减乘除)

    package com.qcloud.component.publicservice.util; import java.math.BigDecimal; /** * 由于Java的简单类型不能够精确 ...

  3. BigDecimal除法运算出现java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result的解决办法

    BigDecimal除法运算出现java.lang.ArithmeticException: Non-terminating decimal expansion; no exact represent ...

  4. java中小数的处理:高精度运算用bigDecimal类,精度保留方法,即舍入方式的指定

    一. 计算机的小数计算一定范围内精确,超过范围只能取近似值: 计算机存储的浮点数受存储bit位数影响,只能保证一定范围内精准,超过bit范围的只能取近似值. java中各类型的精度范围参见:http: ...

  5. Bigdecimal除法异常

    1.异常信息摘要(详细请见文末): java.lang.ArithmeticException: Non-terminating decimal expansion; no exact represe ...

  6. float、double、BigDecimal的一些精度问题

    float f = 280.8f;System.out.println(f*100);结果是什么?结果是:28080.0f(我是这么想的)实际结果是:28079.998 既然float处理有问题换do ...

  7. php大数除法保留精度问题

    有人在群里问大数除法,要求保留精度的问题,发现普通的方法都不能保存精度,最后找了一下资料发现可以这样 这倒是个冷门知识,嗯哼

  8. BigDecimal除法问题

    BigDecimal类的主要功能是进行小数的大数计算,而且最重要的是可以精确到指定的四舍五入位数. 如果要进行四舍五入的操作,则必须依靠以下的方法:public BigDecimal divide(B ...

  9. BigDecimal除法

    public class TestDemo { public static void main(String[] args) { BigDecimal dataValue = new BigDecim ...

随机推荐

  1. Nginx 作为静态资源服务器

    Nginx Windows 版本的启动停止,重新加载配置 启动 Windows版本下载解压后有一个nginx.exe可执行文件,双击启动. 启动后 浏览器访问http://127.0.0.1 可以看到 ...

  2. GIT 报错:Result too large 解决办法

    在使用bower install命令下载前端依赖的js插件时,git出错了,报错信息如下: bower ECMDERR Failed to execute "git ls-remote -- ...

  3. 微信小程序跳H5页面

    主页面:index.wxml 主页面:index.js ↑跳转到另一个wxml页面→recharge.wxml recharge.wxml web-view中设置跳转h5的链接,可以加上需要的参数: ...

  4. [Medium翻译]RESTful API权威设计指南-设计更好的API

    本文为授权译文.希望查看原文的同学请戳链接:https://hackernoon.com/restful-api-design-step-by-step-guide-2f2c9f9fcdbf 对于我们 ...

  5. Notes 20180306 : 变量与常量

    1.1 变量与常量 我们在开发中会经常听到常量和变量,那么常量和变量指的又是什么呢?顾名思义,在程序执行过程中,其值不能被改变的量称为常量,其值能被改变的量称为变量.变量与常量的命名都必须使用合法的标 ...

  6. DB数据源之SpringBoot+MyBatis踏坑过程(二)手工配置数据源与加载Mapper.xml扫描

    DB数据源之SpringBoot+MyBatis踏坑过程(二)手工配置数据源与加载Mapper.xml扫描 liuyuhang原创,未经允许进制转载  吐槽之后应该有所改了,该方式可以作为一种过渡方式 ...

  7. .Net core 使用Jenkins + Docker + Azure Devops 傻瓜式部署

    这几天一直在搞 Jenkins + Docker + Azure Devops 部署,因为这种方式部署真的非常的省心,而且速度快,方便快捷,等等无数优点,感谢我的单身领导的支持,当然也感谢 晓晨大神, ...

  8. springsource-tool-suite插件下载

    下载地址:    https://spring.io/tools3/sts/all/ 下载页面上的 update sites archives文件

  9. Spring的jar包不同版本的下载地址

    http://repo.spring.io/release/org/springframework/spring/ 可以直接下载不同版本的spring jar包

  10. TinyMCE:下载、安装、配置

    第一步:下载 官网下载:https://www.tiny.cloud/download/ TinyMCE从4.0开始,不再支持直接下载,而是直接使用提供免费的CDN,让用户免除安装过程,可以在网站中使 ...