zoukankan      html  css  js  c++  java
  • Java关于BigDecimal的介绍及用法(二)

    一、BigDecimal格式化

    由于NumberFormat类的format()方法可以使用BigDecimal对象作为其参数,可以利用BigDecimal对超出16位有效数字的货币值,百分值,以及一般数值进行格式化控制。

    以利用BigDecimal对货币和百分比格式化为例。首先,创建BigDecimal对象,进行BigDecimal的算术运算后,分别建立对货币和百分比格式化的引用,最后利用BigDecimal对象作为format()方法的参数,输出其格式化的货币值和百分比。

    @Test
        public void test8(){
            NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用
            NumberFormat percent = NumberFormat.getPercentInstance();   //建立百分比格式化引用
            percent.setMaximumFractionDigits(3); //百分比小数点最多3位
    
            BigDecimal money = new BigDecimal("15000.48"); // 合同金额
            BigDecimal taxRate = new BigDecimal("0.008");  // 税率
            BigDecimal taxRateMoney = money.multiply(taxRate);   // 利息
    
            System.out.println("合同金额: " + currency.format(money));
            System.out.println("税率: " + percent.format(taxRate));
            System.out.println("利息: " + currency.format(taxRateMoney));
        }

    结果:

    BigDecimal格式化保留2为小数,不足则补0:

    @Test
        public void test9(){
            System.out.println(formatToNumber(new BigDecimal("3.435")));
            System.out.println(formatToNumber(new BigDecimal(0)));
            System.out.println(formatToNumber(new BigDecimal("0.00")));
            System.out.println(formatToNumber(new BigDecimal("0.001")));
            System.out.println(formatToNumber(new BigDecimal("0.006")));
            System.out.println(formatToNumber(new BigDecimal("0.206")));
        }
        /**
         * @desc 1.0~1之间的BigDecimal小数,格式化后失去前面的0,则前面直接加上0。
         * 2.传入的参数等于0,则直接返回字符串"0.00"
         * 3.大于1的小数,直接格式化返回字符串
         * @param obj 传入的小数
         * @return
         */
        public static String formatToNumber(BigDecimal obj) {
            DecimalFormat df = new DecimalFormat("#.00");
            if(obj.compareTo(BigDecimal.ZERO)==0) {
                return "0.00";
            }else if(obj.compareTo(BigDecimal.ZERO)>0&&obj.compareTo(new BigDecimal(1))<0){
                return "0"+df.format(obj).toString();
            }else {
                return df.format(obj).toString();
            }
        }

    结果为:

    二、BigDecimal常见异常

    2.1、除法divide() 出现异常

    使用除法函数在divide的时候要设置各种参数,要有除数、精确的小数位数和舍入模式,不然会出现报错。源码如下:

    注意这个BigDecimal的divide方法有两个重载的方法,一个是传两个参数的,一个是传三个参数的:

    两个参数的方法:

    @param divisor value by which this {@code BigDecimal} is to be divided. 传入除数

    @param roundingMode rounding mode to apply. 传入round的模式

    三个参数的方法:

    @param divisor value by which this {@code BigDecimal} is to be divided. 传入除数
    @param scale scale of the {@code BigDecimal} quotient to be returned. 传入精度
    @param roundingMode rounding mode to apply. 传入round的模式

    代码演示:

    @Test
        public void test10(){
            BigDecimal Dividend = new BigDecimal("1");
            BigDecimal divisor = new BigDecimal("3");
    
            BigDecimal res1 = Dividend.divide(divisor,3,BigDecimal.ROUND_UP);
            System.out.println("除法ROUND_UP:"+res1);
            BigDecimal res2 = Dividend.divide(divisor,3,BigDecimal.ROUND_DOWN);
            System.out.println("除法ROUND_DOWN:"+res2);
            BigDecimal res3 = Dividend.divide(divisor,3,BigDecimal.ROUND_CEILING);
            System.out.println("除法ROUND_CEILING:"+res3);
            BigDecimal res4 = Dividend.divide(divisor,3,BigDecimal.ROUND_FLOOR);
            System.out.println("除法ROUND_FLOOR:"+res4);
            BigDecimal res5 = Dividend.divide(divisor,3,BigDecimal.ROUND_HALF_UP);
            System.out.println("除法ROUND_HALF_UP:"+res5);
            BigDecimal res6 = Dividend.divide(divisor,3,BigDecimal.ROUND_HALF_DOWN);
            System.out.println("除法ROUND_HALF_DOWN:"+res6);
            BigDecimal res7 = Dividend.divide(divisor,3,BigDecimal.ROUND_HALF_EVEN);
            System.out.println("除法ROUND_HALF_EVEN:"+res7);
            BigDecimal res8 = Dividend.divide(divisor,3,BigDecimal.ROUND_UNNECESSARY);
            System.out.println("除法ROUND_UNNECESSARY:"+res8);
        }

    结果展示:

     

    原因分析:

    断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。

    2.2、除法divide() 出现异常

    java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result

    原因分析:

      原来JAVA中如果用BigDecimal做除法的时候一定要在divide方法中传递第二个参数,定义精确到小数点后几位,否则在不整除的情况下,结果是无限循环小数时,就会抛出以下异常。java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

    解决方法:

    a.divide(b, 2, BigDecimal.ROUND_HALF_UP);

    三、BigDecimal的八种舍入模式

    @Test
        public void test11(){
            BigDecimal a = new BigDecimal("1.12345");
            System.out.println("a = " + a);
    
            BigDecimal b = a.setScale(4,BigDecimal.ROUND_HALF_DOWN);
            System.out.println("b = " + b);
    
            BigDecimal c = a.setScale(4,BigDecimal.ROUND_HALF_UP);
            System.out.println("c = " + c);
        }

    3.1、ROUND_UP

    舍入远离零的舍入模式(向远离0的方向舍入)。

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

    注意,此舍入模式始终不会减少计算值的大小。

    3.2、ROUND_DOWN

    接近零的舍入模式(向零方向舍入)。

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

    注意,此舍入模式始终不会增加计算值的大小。

    3.3、ROUND_CEILING

    接近正无穷大的舍入模式(向正无穷方向舍入)。

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

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

    注意,此舍入模式始终不会减少计算值。

    3.4、ROUND_FLOOR

    接近负无穷大的舍入模式(向负无穷方向舍入)。

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

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

    注意,此舍入模式始终不会增加计算值。

    3.5、ROUND_HALF_UP

    向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式(向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向上舍入, 1.55保留一位小数结果为1.6)。

    如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。

    注意,这是我们大多数人在小学时就学过的舍入模式(四舍五入)。

    3.6、ROUND_HALF_DOWN

    向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式

    (向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向下舍入, 例如1.55 保留一位小数结果为1.5)。

    如果舍弃部分 > 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同(五舍六入)。

    3.7、ROUND_HALF_EVEN

    向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。

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

    如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同

    (向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,如果保留位数是奇数,使用ROUND_HALF_UP ,如果是偶数,使用ROUND_HALF_DOWN)。

    注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。

    此舍入模式也称为“银行家舍入法”,主要在美国使用。四舍六入,五分两种情况。

    如果前一位为奇数,则入位,否则舍去。

    以下例子为保留小数点1位,那么这种舍入方式下的结果。

    1.15>1.2 1.25>1.2

    3.8、ROUND_UNNECESSARY

    断言请求的操作具有精确的结果,因此不需要舍入(计算结果是精确的,不需要舍入模式)。

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

    四、BigDecimal总结

    4.1、总结

    1. 在需要精确的小数计算时再使用BigDecimal,BigDecimal的性能比double和float差,在处理庞大,复杂的运算时尤为明显。故一般精度的计算没必要使用BigDecimal。
    2. 尽量使用参数类型为String的构造函数。
    3. BigDecimal都是不可变的(immutable)的, 在进行每一次四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得要保存操作后的值。

    4.2、工具类推荐

    ArithmeticUtils

     五、请关注后续博客

    记得点波收藏o!!!

    博客首页:https://www.cnblogs.com/liyhbk/

  • 相关阅读:
    Python 猜数小程序(练习)
    Mysql 字符串日期互转
    MaxCompute 语句笔记
    数据仓库架构
    Python 比较两个字符串的相似度
    Python print
    Python简单计算器
    HashMap为什么线程不安全(死循环+数据丢失过程分析)
    浅谈ArrayList、Vector和LinkedList
    JAVA对象的浅克隆和深克隆
  • 原文地址:https://www.cnblogs.com/liyhbk/p/13858575.html
Copyright © 2011-2022 走看看