BigDecimal详解


前言

最近项目中有个需求,需要将库中某个字段的值累加,并精确到小数点后两位,返回前端显示,开始使用的是Double去实现,没想到出了问题,Double数据类型在进行累加操作的时候会丢失精度,所以数据显示的时候,输出的是一个小数点后很长的数据,因此果断改为BigDeciml操作,刚好记录一下BigDemical的一些常用操作。


一、BigDecimal类

根据Java8中文手册,Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。

二、常用方法

1.构造方法

  • BigDecimal(int) 创建一个具有参数所指定整数值的对象。
  • BigDecimal(double) 创建一个具有参数所指定双精度值的对象。(不建议采用)
  • BigDecimal(long) 创建一个具有参数所指定长整数值的对象。
  • BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象

构造方法一共有四种,其中使用double声明一个BigDecimal类时,尽量不要直接传入Double的值,因为这样构造的BigDemical是不可预知的,建议使用String.valueOf(number)传入

2.基本的运算

加法

使用add方法如下:

BigDecimal num1 = new BigDecimal(123);
log.info("结果为:{}",num1.add(new BigDecimal(456)))

如果被加的数是一个Double类型的数据建议使用以下写法:

BigDecimal num1 = new BigDecimal(123);
log.info("结果为:{}",num1.add(new BigDecimal(String.valueOf(0.45))))

先将数据转化为字符串,然后将使用构造方法
如果执行加法以后还要对结果进行舍入,那么可以做以下处理:

BigDecimal num1 = new BigDecimal(123);
log.info("结果为:{}",num1.add(new BigDecimal(String.valueOf(0.45),BigDecimal.ROUND_CEILING)))

减法

使用subtract方法

BigDecimal num1 = new BigDecimal(456);
log.info("结果为:{}",num1.subtract(new BigDecimal(123)))

Double类型同上
同样也可以进行舍入

BigDecimal num1 = new BigDecimal(123);
log.info("结果为:{}",num1.subtract(new BigDecimal(String.valueOf(0.45),BigDecimal.ROUND_CEILING)))

乘法

使用multiply方法

BigDecimal num1 = new BigDecimal(456);
log.info("结果为:{}",num1.multiply(new BigDecimal(123)))

Double类型同加法
同样也可以进行舍入

BigDecimal num1 = new BigDecimal(123);
log.info("结果为:{}",num1.multiply(new BigDecimal(String.valueOf(0.45),BigDecimal.ROUND_CEILING)))

除法

使用divide方法

BigDecimal num1 = new BigDecimal(456);
log.info("结果为:{}",num1.divide(new BigDecimal(123)))

同样也可以进行舍入

BigDecimal num1 = new BigDecimal(123);
log.info("结果为:{}",num1.divide(new BigDecimal(String.valueOf(0.45),BigDecimal.ROUND_CEILING)))

3.保留小数(精确到几位)

可以使用divide方法对一个BigDecimal类型的数据进行保留几位小数的处理
例如:

BigDecimal divisor = new BigDecimal(1000);
BigDecimal num = new BigDecimal(4561.2564);
num.divide(divisor, 2, BigDecimal.ROUND_CEILING)
log.info("原来的数除以1000保留两位小数:{}",num1.divide(new BigDecimal(String.valueOf(0.45),BigDecimal.ROUND_CEILING)))

或者只保留两位小数

BigDecimal num = new BigDecimal(4561.2564);
num.setScale(2, BigDecimal.ROUND_CEILING)
log.info("原来的数除以1000保留两位小数:{}",num1.divide(new BigDecimal(String.valueOf(0.45),BigDecimal.ROUND_CEILING)))

4.舍入的类型

这里只记录可能会用到的几种类型

ROUND_UP向上舍入

无论正负,只要大于都会进一

 BigDecimal num = new BigDecimal(String.valueOf(1.4));
 System.out.println(num.setScale(0, BigDecimal.ROUND_UP));
 //2
 BigDecimal num1 = new BigDecimal(String.valueOf(-1.4));
 System.out.println(num1.setScale(0, BigDecimal.ROUND_UP));
 //-2
ROUND_DOWN向下舍入

无论正负,都会舍去

BigDecimal num = new BigDecimal(String.valueOf(1.4));
System.out.println(num.setScale(0, BigDecimal.ROUND_DOWN));
//1
BigDecimal num1 = new BigDecimal(String.valueOf(-1.4));
System.out.println(num1.setScale(0, BigDecimal.ROUND_DOWN));
//-1
ROUND_CEILING正向舍入

是 ROUND_UP 和ROUND_DOWN 的组合,如果 BigDecimal 为正数,则行为与 ROUND_UP 相同;如果 BigDecimal 为负数,则行为与 ROUND_DOWN 相同

BigDecimal num = new BigDecimal(String.valueOf(1.4));
System.out.println(num.setScale(0, BigDecimal.ROUND_CEILING));
//2
BigDecimal num1 = new BigDecimal(String.valueOf(-1.4));
System.out.println(num1.setScale(0, BigDecimal.ROUND_CEILING));
//-1
ROUND_FLOOR负向舍入

ROUND_UP 和 ROUND_DOWN 的组合,但是和ROUND_CEILING 是相反的。如果 BigDecimal 为正数,则行为与 ROUND_DOWN 相同;如果为负数,则行为与 ROUND_UP 相同

BigDecimal num = new BigDecimal(String.valueOf(1.4));
System.out.println(num.setScale(0, BigDecimal.ROUND_FLOOR));
//1
BigDecimal num1 = new BigDecimal(String.valueOf(-1.4));
System.out.println(num1.setScale(0, BigDecimal.ROUND_FLOOR));
//-2
ROUND_HALF_UP四舍五入

这个就是我们经常使用的,不再解释

BigDecimal num = new BigDecimal(String.valueOf(1.4));
System.out.println(num.setScale(0, BigDecimal.ROUND_HALF_UP));
//1
BigDecimal num1 = new BigDecimal(String.valueOf(-1.4));
System.out.println(num1.setScale(0, BigDecimal.ROUND_HALF_UP));
//-1
BigDecimal num2 = new BigDecimal(String.valueOf(1.5));
System.out.println(num2.setScale(0, BigDecimal.ROUND_HALF_UP));
//2
BigDecimal num3 = new BigDecimal(String.valueOf(-1.5));
System.out.println(num3.setScale(0, BigDecimal.ROUND_HALF_UP));
//-2
ROUND_HALF_DOWN五舍六入

大于6就进一,小于6就舍去

BigDecimal num = new BigDecimal(String.valueOf(1.6));
System.out.println(num.setScale(0, BigDecimal.ROUND_HALF_DOWN));
//2
BigDecimal num1 = new BigDecimal(String.valueOf(-1.6));
System.out.println(num1.setScale(0, BigDecimal.ROUND_HALF_DOWN));
//-2
BigDecimal num2 = new BigDecimal(String.valueOf(1.5));
System.out.println(num2.setScale(0, BigDecimal.ROUND_HALF_DOWN));
//1
BigDecimal num3 = new BigDecimal(String.valueOf(-1.5));
System.out.println(num3.setScale(0, BigDecimal.ROUND_HALF_DOWN));
//-1

5.次方

使用pow函数

BigDecimal num = new BigDecimal("1.02");
num.pow(2)
//即是num的二次方,pow中填入的是一个整数

6.开平方

这个方法也是在网上搜到的,因为BigDecimal并没有提供开平方的工具类,当然,也可以转为其他数据类型,开方之后再转换
使用牛顿迭代法去开方

/**
     * BigDecimal开方
     * @param num
     * @return
     */
    public static BigDecimal sqrt(BigDecimal num) {
        if(num.compareTo(BigDecimal.ZERO) < 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal x = num.divide(new BigDecimal("2"), MathContext.DECIMAL128);
        while(x.subtract(x = sqrtIteration(x, num)).abs().compareTo(new BigDecimal("0.0000000000000000000001")) > 0);
        return x;
    }

    private static BigDecimal sqrtIteration(BigDecimal x, BigDecimal n) {
        return x.add(n.divide(x, MathContext.DECIMAL128)).divide(new BigDecimal("2"), MathContext.DECIMAL128);
    }

总结

BigDecimal在业务需求比较精准的情况下,是非常必要的,可以避免基本数据类型产生的bug

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值