BigDecimal除法的精度问题

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

I. 问题抛出

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

  1. @Test
  2. public void testBigDecimal() {
  3. BigDecimal origin = new BigDecimal(541253);
  4. BigDecimal now = new BigDecimal(12389431);
  5. BigDecimal val = origin.divide(now, RoundingMode.HALF_UP);
  6. System.out.println(val);
  7. origin = new BigDecimal(541253);
  8. now = new BigDecimal(12389431.3);
  9. val = origin.divide(now, RoundingMode.HALF_UP);
  10. System.out.println(val);
  11. origin = new BigDecimal(541253.4);
  12. now = new BigDecimal(12389431);
  13. val = origin.divide(now, RoundingMode.HALF_UP);
  14. System.out.println(val);
  15. }

上面的输出是什么 ?

  1. 0
  2. 0
  3. 0.043686703610520937021487456961257

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

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

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

输出结果为:

  1. 0.04369

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

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

II. 源码定位

1. 整形传参构造

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

  1. BigDecimal origin = new BigDecimal(541253);

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

  1. // java.math.BigDecimal#BigDecimal(int)
  2. public BigDecimal(int val) {
  3. this.intCompact = val;
  4. this.scale = 0;
  5. this.intVal = null;
  6. }
  7. public BigDecimal(long val) {
  8. this.intCompact = val;
  9. this.intVal = (val == INFLATED) ? INFLATED_BIGINT : null;
  10. this.scale = 0;
  11. }

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

2. 浮点传参

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

  1. @Test
  2. public void testBigDecimal() {
  3. BigDecimal origin = new BigDecimal(541253.0);
  4. BigDecimal now = new BigDecimal(12389431.1);
  5. BigDecimal tmp = new BigDecimal(0.0);
  6. }

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

3. String传参

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

  1. @Test
  2. public void testBigDecimal() {
  3. BigDecimal origin = new BigDecimal("541253.0");
  4. BigDecimal now = new BigDecimal("12389431.1");
  5. BigDecimal t = new BigDecimal("0.0");
  6. }

上面三个的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. 清除IE下input的叉叉

    很多时候,我们在开发过程中,设计师会在输入框后加上定制的清除图标,但是在IE下有IE自带的清除图标,肯定是不美观的. <style> ::-ms-clear, ::-ms-reveal{d ...

  2. 关闭 XXXXX 前你必须关闭所有会话框

    这个问题应该是和Microsoft管理控制台(Microsoft Management Console,MMC)有关系 我是在使用 任务计划程序 时碰到这个问题的,网上也有其他人在使用 事件查看器 的 ...

  3. Selenium图片上传

    方式1: 如果是input类型的标签则可直接赋值 部分代码: driver.find_element_by_name("file").send_keys("E:\\tes ...

  4. 删除oracle实例

    1.在开始菜单中,点击ORAHOME目录下的"Configuration and Migration Tools"下的"Database Configuration As ...

  5. java _this关键字的用法

    1:This关键字可以用于从一个构造方法调用另一个构造方法,可以用于避免重复代码 2:this的第二个用于this.xxx表示成员变量,成员变量的作用范围是  类   避免产生歧义 package c ...

  6. VB.NET的一个邮件发送函数

    ''' <summary> ''' VB.NET邮件发送程序 ''' 还没用在别的服务器,不晓得能不能行,慎用! ''' </summary> ''' <param na ...

  7. DB数据源之SpringBoot+MyBatis踏坑过程(四)没有使用连接池的后果

    DB数据源之SpringBoot+MyBatis踏坑过程(四)没有使用连接池的后果 liuyuhang原创,未经允许禁止转载  系列目录连接 DB数据源之SpringBoot+Mybatis踏坑过程实 ...

  8. ES5 实现 ES6 的 class以及extends

    ts中是这样的 class Greeter { greeting:string; constructor(message:string){ this.greeting = message; } gre ...

  9. 个人开发者即时到账收款方案 BufPay.com

    BufPay 个人即时到账支付平台 前言 作为独立开发者,一般只有一个人独立奋战,做出了产品需要收款是非常麻烦的,接入支付宝微信支付都需要公司公户,而注册公司.开公户等一系列操作非常麻烦,成本也很高一 ...

  10. python初学者日记01(字符串操作方法)

    时间:2018/12/16 作者:永远的码农(博客园) 环境: win10,pycharm2018,python3.7.1 1.1  基础操作(交互输入输出) input = input(" ...