• 深入理解 BigDecimal


    什么是 BigDecimal为什么用 BigDecimal 而不用 double加减乘除常用方法保留两位小数四舍五入比较注意事项参考

    什么是 BigDecimal

    BigDecimal 可以表示一个任意大小且精度完全准确的浮点数。

    为什么用 BigDecimal 而不用 double

    Talk is cheap, Show me the Code.

    例 1:

     1double d1 = 0.3;
    2double d2 = 0.2;
    3System.out.println("Double:  0,3 - 0,2 = " + (d1 - d2));
    4
    5float f1 = 0.3f;
    6float f2 = 0.2f;
    7System.out.println("Float:  0,3 - 0,2 = " + (f1 - f2));
    8
    9BigDecimal bd1 = new BigDecimal("0.3");
    10BigDecimal bd2 = new BigDecimal("0.2");
    11System.out.println("BigDec:  0,3 - 0,2 = " + (bd1.subtract(bd2)));

    运行结果

    1Double:     0,3 - 0,2 = 0.09999999999999998
    2Float:     0,3 - 0,2 = 0.10000001
    3BigDec:     0,3 - 0,2 = 0.1

    从运行结果可以得出,当我们要做精确的小数操作运算时,就需要用到 BigDecimal。那下面做一下除法运算,看看结果:

    例 2:

     1double d1 = 10;
    2double d2 = 3;
    3System.out.println("Double:  10 / 3 = " + (d1 / d2));
    4
    5float f1 = 10f;
    6float f2 = 3f;
    7System.out.println("Float:  10 / 3 = " + (f1 / f2));
    8
    9// Exception!
    10BigDecimal bd3 = new BigDecimal("10");
    11BigDecimal bd4 = new BigDecimal("3");
    12System.out.println("BigDec:  10 / 3 = " + (bd3.divide(bd4)));

    运行结果

    1Double:     10 / 3 = 3.3333333333333335
    2Float:     10 / 3 = 3.3333333
    3Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

    当结果除不进,并且没有设置进位的状态值,那就会抛出异常。正确的操作如下:

    1System.out.println("BigDec:	 10 / 3 = " + (bd3.divide(bd4,4,BigDecimal.ROUND_HALF_UP)));

    运行结果

    1Double:     10 / 3 = 3.3333333333333335
    2Float:     10 / 3 = 3.3333333
    3BigDec:     10 / 3 = 3.3333

    总结:当我们在精度要求非常高的时候,需要进行精确的计算,比如:货币,那我们就需要采用 java.math.BigDecimal 类来进行精确计算。

    加减乘除

    方法叙述
    add(BigDecimal) BigDecimal对象中的值相加,然后返回这个对象
    subtract(BigDecimal) BigDecimal对象中的值相减,然后返回这个对象
    multiply(BigDecimal) BigDecimal对象中的值相乘,然后返回这个对象
    divide(BigDecimal) BigDecimal对象中的值相除,然后返回这个对象
     1public class BigDecimalCalculation {
    2    static BigDecimal a = new BigDecimal("0.02");
    3    static BigDecimal b = new BigDecimal("0.03");
    4
    5    public static void main(String[] args) {
    6        System.out.println("a + b = " + a.add(b));
    7        System.out.println("a - b = " + a.subtract(b));
    8        System.out.println("a * b = " + a.multiply(b));
    9        System.out.println("a ÷ b = " + a.divide(b,2,BigDecimal.ROUND_HALF_UP));
    10    }
    11
    12}

    运行结果:

    1a + b = 0.05
    2a - b = -0.01
    3a * b = 0.0006
    4a ÷ b = 0.67

    常用方法

    保留两位小数

     1public class keepTwoDecimal {
    2    public static void main(String[] args) {
    3        BigDecimal num= new BigDecimal(13.154215);
    4
    5        //方式一
    6        DecimalFormat df1 = new DecimalFormat("0.00");
    7        String str = df1.format(num);
    8        System.out.println(str);  //13.15
    9
    10        //方式二
    11        // #.00 表示两位小数 #.0000四位小数
    12        DecimalFormat df2 =new DecimalFormat("#.00");
    13        String str2 =df2.format(num);
    14        System.out.println(str2);  //13.15
    15
    16        //方式三
    17        //%.2f %. 表示 小数点前任意位数   2 表示两位小数 格式后的结果为f 表示浮点型
    18        String result = String.format("%.2f", num);
    19        System.out.println(result);  //13.15
    20    }
    21}

    四舍五入

    • ROUND_UP

    舍入远离零的舍入模式。

    在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数字加 1)

    例如:2.36 -> 2.4

    • ROUND_DOWN

    接近零的舍入模式。

    在丢弃某部分之前始终不增加数字(从不对舍弃部分前面的数字加1,即截短)。

    例如:2.36 -> 2.3

    • ROUND_CEILING

    接近正无穷大的舍入模式。

    如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同;

    如果为负,则舍入行为与 ROUND_DOWN 相同。

    相当于是 ROUND_UPROUND_DOWN 的合集

    • ROUND_FLOOR

    接近负无穷大的舍入模式。

    如果 BigDecimal 为正,则舍入行为与 ROUND_DOWN 相同;

    如果为负,则舍入行为与 ROUND_UP 相同。

    ROUND_CEILING 正好相反

    • ROUND_HALF_UP

    四舍五入

    例如:2.35 -> 2.4

    • ROUND_HALF_DOWN

    五舍六入

    例如:2.35 -> 2.3

    • ROUND_HALF_EVEN

    如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同(四舍五入);

    如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同(五舍六入)。

    例如:1.15 -> 1.1,1.25 -> 1.2

    • ROUND_UNNECESSARY

    断言请求的操作具有精确的结果,因此不需要舍入。

    如果对获得精确结果的操作指定此舍入模式,则抛出 ArithmeticException。

    我觉得剩下得了解下就行,而且我感觉剩下有的就是错的,比如 ROUND_HALF_DOWNROUND_HALF_EVEN,看下面的结果你就知道我说的是为什么了。

     1public class BigDecimalScaleTest {
    2    public static void main(String[] args) {
    3        double num = 2.35;
    4        BigDecimal b = new BigDecimal(num);
    5        // setScale(1) 表示保留一位小数
    6        System.out.println("ROUND_UP,结果:" + b.setScale(1, BigDecimal.ROUND_UP).doubleValue());
    7        System.out.println("ROUND_DOWN,结果:" + b.setScale(1, BigDecimal.ROUND_DOWN).doubleValue());
    8        System.out.println("ROUND_CEILING,结果:" + b.setScale(1, BigDecimal.ROUND_CEILING).doubleValue());
    9        System.out.println("ROUND_FLOOR,结果:" + b.setScale(1, BigDecimal.ROUND_FLOOR).doubleValue());
    10        System.out.println("ROUND_HALF_UP,结果:" + b.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue());
    11        System.out.println("ROUND_HALF_DOWN,结果:" + b.setScale(1, BigDecimal.ROUND_HALF_DOWN).doubleValue());
    12        System.out.println("ROUND_HALF_EVEN,结果:" + b.setScale(1, BigDecimal.ROUND_HALF_EVEN).doubleValue());
    13        System.out.println("ROUND_UNNECESSARY,结果:" + b.setScale(1, BigDecimal.ROUND_UNNECESSARY).doubleValue());
    14    }
    15}

    运行结果

    1ROUND_UP,结果:2.4
    2ROUND_DOWN,结果:2.3
    3ROUND_CEILING,结果:2.4
    4ROUND_FLOOR,结果:2.3
    5ROUND_HALF_UP,结果:2.4
    6ROUND_HALF_DOWN,结果:2.4 (来给我解释解释这个,说好的五舍六入呢)
    7ROUND_HALF_EVEN,结果:2.4 (还有这个)
    8Disconnected from the target VM, address: '127.0.0.1:59637', transport: 'socket'
    9Exception in thread "main" java.lang.ArithmeticException: Rounding necessary

    小结:常用的就是 ROUND_HALF_UPROUND_UPROUND_DOWN,其它的当个笑话就行

    比较

    a.compareTo(b)

    a > b 返回 1;a = b 返回 0;a < b 返回 -1

     1public class BigDecimalCompare {
    2    public static void main(String[] args) {
    3        BigDecimal a = new BigDecimal("0.02");
    4        BigDecimal b = new BigDecimal("0.01");
    5        BigDecimal a2 = new BigDecimal("0.02");
    6
    7        System.out.println(" a > b 返回结果:" + a.compareTo(b));
    8        System.out.println(" a = a2 返回结果:" + a.compareTo(a2));
    9        System.out.println(" b < a 返回结果:" + b.compareTo(a));
    10    }
    11}

    运行结果

    1 a > b 返回结果:1
    2 a = a2 返回结果:0
    3 b < a 返回结果:-1

    注意事项

    在上面的使用中,我们都用的 String 给 BigDecimal 进行 赋值,而没有使用 double 类型赋值,具体的原因看下面的例子:

     1public class BigDecimalTest {
    2    public static void main(String[] args) {
    3        BigDecimal num1 = new BigDecimal(0.005);
    4        BigDecimal num2 = new BigDecimal(1000000);
    5        BigDecimal num3 = new BigDecimal(-1000000);
    6        //尽量用字符串的形式初始化
    7        BigDecimal num12 = new BigDecimal("0.005");
    8        BigDecimal num22 = new BigDecimal("1000000");
    9        BigDecimal num32 = new BigDecimal("-1000000");
    10
    11        //加法
    12        BigDecimal result1 = num1.add(num2);
    13        BigDecimal result12 = num12.add(num22);
    14        //减法
    15        BigDecimal result2 = num1.subtract(num2);
    16        BigDecimal result22 = num12.subtract(num22);
    17        //乘法
    18        BigDecimal result3 = num1.multiply(num2);
    19        BigDecimal result32 = num12.multiply(num22);
    20        //绝对值
    21        BigDecimal result4 = num3.abs();
    22        BigDecimal result42 = num32.abs();
    23        //除法
    24        BigDecimal result5 = num2.divide(num1,20,BigDecimal.ROUND_HALF_UP);
    25        BigDecimal result52 = num22.divide(num12,20,BigDecimal.ROUND_HALF_UP);
    26
    27        System.out.println("加法用value结果:"+result1);
    28        System.out.println("加法用string结果:"+result12);
    29
    30        System.out.println("减法value结果:"+result2);
    31        System.out.println("减法用string结果:"+result22);
    32
    33        System.out.println("乘法用value结果:"+result3);
    34        System.out.println("乘法用string结果:"+result32);
    35
    36        System.out.println("绝对值用value结果:"+result4);
    37        System.out.println("绝对值用string结果:"+result42);
    38
    39        System.out.println("除法用value结果:"+result5);
    40        System.out.println("除法用string结果:"+result52);
    41    }
    42  }

    运行结果:

     1加法用value结果:1000000.005000000000000000104083408558608425664715468883514404296875
    2加法用string结果:1000000.005
    3减法value结果:-999999.994999999999999999895916591441391574335284531116485595703125
    4减法用string结果:-999999.995
    5乘法用value结果:5000.000000000000104083408558608425664715468883514404296875000000
    6乘法用string结果:5000.000
    7绝对值用value结果:1000000
    8绝对值用string结果:1000000
    9除法用value结果:199999999.99999999583666365766
    10除法用string结果:200000000.00000000000000000000
    • System.out.println() 中的数字默认是 double 类型的,double 类型的小数计算不准确
    • 使用 BigDecimal 的构造方法传入 double 类型时,计算的结果也是不准确的!

    所以我们在使用 BigDecimal 进行赋值的时候,最好使用传入 String 的构造函数,可以确认精度。

    参考

    https://blog.csdn.net/haiyinshushe/article/details/82721234

    https://www.jianshu.com/p/2947868d76eb

    https://blog.csdn.net/ochangwen/article/details/51531866

  • 相关阅读:
    【计算机视觉】基于自组织背景减除的运动目标检测算法
    【计算机视觉】背景建模之PBAS
    【计算机视觉】背景建模之PBAS
    【计算机视觉】【并行计算与CUDA开发】GPU硬解码---DXVA
    【计算机视觉】【并行计算与CUDA开发】GPU硬解码---DXVA
    【计算机视觉】【并行计算与CUDA开发】GPU硬编码
    【计算机视觉】【并行计算与CUDA开发】GPU硬编码
    【计算机视觉】【并行计算与CUDA开发】GPU硬解码---CUVID
    【计算机视觉】【并行计算与CUDA开发】GPU硬解码---CUVID
    【计算机视觉】【并行计算与CUDA开发】OpenCV中GPU模块使用
  • 原文地址:https://www.cnblogs.com/ferryman/p/12026542.html
Copyright © 2020-2023  润新知