使用BigDecimal进行精确计算

Java中float和double两种基本浮点类型的浮点数存在精度缺失问题(不只是Java,其他语言也是),所以一般在对精度要求较高的计算中(如金融中货币金额的计算)是绝对不可以使用float和double进行精确计算,需要使用BigDecimal进行计算。

有大量的构造器和方法可以用于创建BigDecimal对象,这里就不一一列举了,可以查看API文档或源码,归纳来说主要分3类:

1、BigDecimal(double val)

这个是不推荐使用的,因为使用该构造器有一定的精度问题,如new BigDecimal(0.3),实际传入构造器的并不是0.3,而是一个接近的值。当然一般计算后再转回double类型可能结果没问题精度未确实,但是并不能成为使用的理由,这是个危险的做法,不要使用。

2、BigDecimal(String val)

建议优先使用基于String的构造器,因为不会有精度问题,new BigDecimal("0.3"),实际传入的就是0.3。

通过接口传递金额等参数时,最好也使用String类型,不要使用float和double两种基本浮点类型,可以避免精度缺失。

3、BigDecimal valueOf(double val)

如果确实要使用double类型,不要使用对应的构造器,就使用valueOf静态方法来创建BigDecimal对象。

BigDecimal 提供了一系列的对精确浮点数进行计算的方法,可以用来精确计算,还有将计算结果重新转换成其他类型数据的方法,详情见API文档或源码,这里提供一个例子,一般一些公司会自己封装工具类或者使用第三方jar包中的工具类进行计算。

如下是个BigDecimal计算的工具类的简单例子(可以根据需要进行调整)。

/**
 * @author zhangbo
 * @date 2019/5/9 21:16
 * @since 1.0
 */
public final class BigDecimalUtil {
    /**
     * 默认除法运算精确度
     */
    private static final int DEFAULT_DIV_DEFINITION = 2;

    /**
     * 加法
     * @param num1 数字1
     * @param num2 数字2
     * @return 和
     */
    public static String add(String num1, String num2) {
        BigDecimal bigDecimal1 = new BigDecimal(num1);
        BigDecimal bigDecimal2 = new BigDecimal(num2);
        return bigDecimal1.add(bigDecimal2).toString();
    }

    /**
     * 加法
     * @param num1 数字1
     * @param num2 数字2
     * @return 和
     */
    public static double add(double num1, double num2) {
        BigDecimal bigDecimal1 = BigDecimal.valueOf(num1);
        BigDecimal bigDecimal2 = BigDecimal.valueOf(num2);
        return bigDecimal1.add(bigDecimal2).doubleValue();
    }

    /**
     * 减法
     * @param num1 数字1
     * @param num2 数字2
     * @return 减法的结果
     */
    public static double subtract(double num1, double num2) {
        BigDecimal bigDecimal1 = BigDecimal.valueOf(num1);
        BigDecimal bigDecimal2 = BigDecimal.valueOf(num2);
        return bigDecimal1.subtract(bigDecimal2).doubleValue();
    }

    /**
     * 乘法法
     * @param num1 数字1
     * @param num2 数字2
     * @return 乘法的结果
     */
    public static double multiply(double num1, double num2) {
        BigDecimal bigDecimal1 = BigDecimal.valueOf(num1);
        BigDecimal bigDecimal2 = BigDecimal.valueOf(num2);
        return bigDecimal1.multiply(bigDecimal2).doubleValue();
    }

    /**
     * 除法
     * @param num1 除数
     * @param num2 被除数
     * @param definition 精确度
     * @return 乘法的结果
     */
    public static double divide(double num1, double num2, Integer definition) {
        BigDecimal bigDecimal1 = BigDecimal.valueOf(num1);
        BigDecimal bigDecimal2 = BigDecimal.valueOf(num2);
        int scale = null == definition ? DEFAULT_DIV_DEFINITION : definition;
        return bigDecimal1.divide(bigDecimal2, scale).doubleValue();
    }

    public static void main(String[] args) {
        double d1 = 0.1;
        double d2 = 0.3;

        System.out.println("使用double作为构造器参数:");
        BigDecimal bigDecimal1 = new BigDecimal(d1);
        BigDecimal bigDecimal2 = new BigDecimal(d2);
        System.out.println("bigDecimal1 = " + bigDecimal1);
        System.out.println("bigDecimal2 = " + bigDecimal2);
        System.out.println("d1 - d2 = " + bigDecimal1.subtract(bigDecimal2).doubleValue());

        System.out.println("使用String作为构造器参数:" + new BigDecimal("0.3"));
        System.out.println("0.1 + 0.3 = " + BigDecimalUtil.add("0.1", "0.3"));

        System.out.println("使用BigDecimal.valueOf方法:" + BigDecimal.valueOf(0.3));
        System.out.println("0.1 + 0.3 = " + BigDecimalUtil.add(d1, d2));
        System.out.println("0.1 - 0.3 = " + BigDecimalUtil.subtract(d1, d2));
        System.out.println("0.1 * 0.3 = " + BigDecimalUtil.multiply(d1, d2));
        System.out.println("0.1 / 0.3 = " + BigDecimalUtil.divide(d1, d2, 3));
    }
}

测试驱动开发,要写好用例,开发,在自测,一般写完代码,就要写对应的junit用例进行验证,确保功能正确性,这里不是实际开发只是练习,直接使用main方法进行快速的验证即可。

结果:

使用double作为构造器参数:
bigDecimal1 = 0.1000000000000000055511151231257827021181583404541015625
bigDecimal2 = 0.299999999999999988897769753748434595763683319091796875
d1 - d2 = -0.19999999999999998
使用String作为构造器参数:0.3
0.1 + 0.3 = 0.4
使用BigDecimal.valueOf方法:0.3
0.1 + 0.3 = 0.4
0.1 - 0.3 = -0.2
0.1 * 0.3 = 0.03
0.1 / 0.3 = 0.3

编者题外话:有幸进入自己喜欢的软件开发行业,编者感觉很荣幸,感谢这一路来各位领导和同事以及朋友们的帮助!工作这些年,一直忙于工作,个人也不够勤快,一直没怎么写博客。本地总结文档管理的也不够好。计划后面多写一些博客,一方面进行学习总结,不断提升自己,方便查阅;另一方面也希望能给他人带来帮助。希望能有志同道合的朋友一起进步,欢迎提出疑问和建议,共同学习讨论和进步。已经入坑,就要砥砺前行,不断学习和进步,总有千般劫难,只当一种快乐的挑战,当你感觉艰难时就是你在进步在突破时!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值