• BigDecimal精确进行数学运算


    问题

    假设现在有个计算公式如下

    public static void getResult(int total, int pbd, int ag, int num) {
            double cl = 10 * Math.log10(pbd);
            double txg = 10 * Math.log10(num);
            double result = total - cl + ag + txg;
    
            logger.info(String.valueOf(result));
        }

    main函数中调用测试

    public static void main(String[] args) {
            int pbd = 20;
            int ag = -5;
            int num = 2;
    
            getResult(23, pbd, ag, num);
            getResult(22, pbd, ag, num);
            getResult(21, pbd, ag, num);
            getResult(20, pbd, ag, num);
            getResult(19, pbd, ag, num);
            getResult(18, pbd, ag, num);
            getResult(17, pbd, ag, num);
            getResult(16, pbd, ag, num);
            
        }

    运行结果如下

    16:29:04.997 [main] INFO com.demo.BigDecimalTest - 8.0
    16:29:05.009 [main] INFO com.demo.BigDecimalTest - 7.0
    16:29:05.009 [main] INFO com.demo.BigDecimalTest - 6.0
    16:29:05.009 [main] INFO com.demo.BigDecimalTest - 5.0
    16:29:05.009 [main] INFO com.demo.BigDecimalTest - 3.9999999999999996
    16:29:05.009 [main] INFO com.demo.BigDecimalTest - 2.9999999999999996
    16:29:05.009 [main] INFO com.demo.BigDecimalTest - 1.9999999999999996
    16:29:05.009 [main] INFO com.demo.BigDecimalTest - 0.9999999999999996

    可以发现,只有total参数按照1个步长递减,后面的参数没有变化,这很明显有些参数计算是不正确的

    pbd和num的对数分别计算出来

    logger.info(String.valueOf(10 * Math.log10(20)));
    logger.info(String.valueOf(10 * Math.log10(2)));
    16:33:18.017 [main] INFO com.demo.BigDecimalTest - 13.010299956639813
    16:33:18.017 [main] INFO com.demo.BigDecimalTest - 3.010299956639812

    那么在total为20和19时的计算公式如下

    20-13.010299956639813-5+3.010299956639812 = 5.0
    19-13.010299956639813-5+3.010299956639812 = 3.9999999999999996

    这两个结果其实都不准确,但为19的时候相对准确度高一点,为20的时候,这个结果肯定不可能刚好为整数,因为小数部分是不同的。

    在验证一个问题

    logger.info(String.valueOf(19-13.0103 -5 + 3.0103));

    这个结果肉眼计算应该是4,但是java运行结果却为3.999999999999999

    16:44:01.118 [main] INFO com.demo.BigDecimalTest - 3.999999999999999

    通过以上现象说明,如果包含小数的运算,直接用java中加减符号是无法做到准确的。

    BigDecimal解决问题

    一、将上面的问题改为BigDecimal进行计算

    public static void getResult2(int total, int pbd, int ag, int num) {
            double cl = 10 * Math.log10(pbd);
            double txg = 10 * Math.log10(num);
    
            BigDecimal result = new BigDecimal(total).subtract(new BigDecimal(cl)).add(new BigDecimal(ag))
                    .add(new BigDecimal(txg));
    
            logger.info(String.valueOf(result.doubleValue()));
        }

    结果如下

    16:49:08.445 [main] INFO com.demo.BigDecimalTest - 8.0
    16:49:08.457 [main] INFO com.demo.BigDecimalTest - 7.0
    16:49:08.458 [main] INFO com.demo.BigDecimalTest - 6.0
    16:49:08.458 [main] INFO com.demo.BigDecimalTest - 5.0
    16:49:08.458 [main] INFO com.demo.BigDecimalTest - 3.9999999999999996
    16:49:08.458 [main] INFO com.demo.BigDecimalTest - 2.9999999999999996
    16:49:08.458 [main] INFO com.demo.BigDecimalTest - 1.9999999999999996
    16:49:08.458 [main] INFO com.demo.BigDecimalTest - 0.9999999999999996

    问题并没有得到解决

    二、结果使用toString输出

    public static void getResult3(int total, int pbd, int ag, int num) {
            double cl = 10 * Math.log10(pbd);
            double txg = 10 * Math.log10(num);
    
            BigDecimal result = new BigDecimal(total).subtract(new BigDecimal(cl)).add(new BigDecimal(ag))
                    .add(new BigDecimal(txg));
    
            logger.info(result.toString());
        }
    16:51:03.310 [main] INFO com.demo.BigDecimalTest - 7.999999999999999555910790149937383830547332763671875
    16:51:03.324 [main] INFO com.demo.BigDecimalTest - 6.999999999999999555910790149937383830547332763671875
    16:51:03.324 [main] INFO com.demo.BigDecimalTest - 5.999999999999999555910790149937383830547332763671875
    16:51:03.324 [main] INFO com.demo.BigDecimalTest - 4.999999999999999555910790149937383830547332763671875
    16:51:03.324 [main] INFO com.demo.BigDecimalTest - 3.999999999999999555910790149937383830547332763671875
    16:51:03.324 [main] INFO com.demo.BigDecimalTest - 2.999999999999999555910790149937383830547332763671875
    16:51:03.324 [main] INFO com.demo.BigDecimalTest - 1.999999999999999555910790149937383830547332763671875
    16:51:03.325 [main] INFO com.demo.BigDecimalTest - 0.999999999999999555910790149937383830547332763671875

    结果正确,而且精确度很高,保留了小数点后51位

    三、将参数转换为字符串传递到BigDecimal构造方法中,使用doubleValue输出

    public static void getResult4(int total, int pbd, int ag, int num) {
            double cl = 10 * Math.log10(pbd);
            double txg = 10 * Math.log10(num);
    
            BigDecimal result = new BigDecimal(String.valueOf(total)).subtract(new BigDecimal(String.valueOf(cl)))
                    .add(new BigDecimal(String.valueOf(ag))).add(new BigDecimal(String.valueOf(txg)));
    
            logger.info(String.valueOf(result.doubleValue()));
        }
    16:54:10.190 [main] INFO com.demo.BigDecimalTest - 7.999999999999999
    16:54:10.202 [main] INFO com.demo.BigDecimalTest - 6.999999999999999
    16:54:10.202 [main] INFO com.demo.BigDecimalTest - 5.999999999999999
    16:54:10.202 [main] INFO com.demo.BigDecimalTest - 4.999999999999999
    16:54:10.202 [main] INFO com.demo.BigDecimalTest - 3.999999999999999
    16:54:10.202 [main] INFO com.demo.BigDecimalTest - 2.999999999999999
    16:54:10.202 [main] INFO com.demo.BigDecimalTest - 1.999999999999999
    16:54:10.202 [main] INFO com.demo.BigDecimalTest - 0.999999999999999

    结果正确,保留了小数点后15位,对一般的场景,保留15位的精度应该已经满足要求了

    四、在上一步基础上,输出时使用toString

    public static void getResult5(int total, int pbd, int ag, int num) {
            double cl = 10 * Math.log10(pbd);
            double txg = 10 * Math.log10(num);
    
            BigDecimal result = new BigDecimal(String.valueOf(total)).subtract(new BigDecimal(String.valueOf(cl)))
                    .add(new BigDecimal(String.valueOf(ag))).add(new BigDecimal(String.valueOf(txg)));
    
            logger.info(result.toString());
        }
    16:57:59.354 [main] INFO com.demo.BigDecimalTest - 7.999999999999999
    16:57:59.367 [main] INFO com.demo.BigDecimalTest - 6.999999999999999
    16:57:59.367 [main] INFO com.demo.BigDecimalTest - 5.999999999999999
    16:57:59.367 [main] INFO com.demo.BigDecimalTest - 4.999999999999999
    16:57:59.367 [main] INFO com.demo.BigDecimalTest - 3.999999999999999
    16:57:59.367 [main] INFO com.demo.BigDecimalTest - 2.999999999999999
    16:57:59.368 [main] INFO com.demo.BigDecimalTest - 1.999999999999999
    16:57:59.368 [main] INFO com.demo.BigDecimalTest - 0.999999999999999

    结果和上一步使用doubleValue相同。

    1、如果有小数计算,尽量使用BigDecimal

    2、使用BigDecimal时,尽量使用字符串传参

  • 相关阅读:
    SVN服务器搭建和使用(一)
    SVN服务器搭建和使用(一)
    lua loadstring与loadfile
    lua loadstring与loadfile
    lua_getstack
    lua_getstack
    让程序在崩溃时体面的退出之Dump文件
    bzoj1054
    poj3678
    poj2749
  • 原文地址:https://www.cnblogs.com/qq931399960/p/13377348.html
Copyright © 2020-2023  润新知