Java中BigDecimal类常用方法,舍入策略

BigDecimal是不可变任意精度有符号 十进制数。

一般是用在大金额结算等对数值精度要求较高的领域(因为浮点数如float、double表示小数的精确度不足,仅可以处理16位有效数字),但其运算性能低于Double、Float等,在一般的工程领域数值计算也不会随便用BigDecimal。

Java BigDecimal 的舍入模式 (RoundingMode)详解

值得注意的是,除法一般都是需要确定保留多少位小数的BigDecimal
bigDecimal = a.divide(b,2,RoundingMode.HALF_UP);,如果不设置保留的小数位Scale,就可能会抛出ArithmeticException异常。这是因为a.divide(b)会默认使用ROUND_UNNECESSARY模式,即不做舍入处理,如果计算结果的小数位和要求保留的小数位不相符,则抛出异常。

下面假设如保留两位小数:(保留两位小数只关心第三位小数),根据第三位小数为0 及<5、=5、>5的情况分析得出如下结论:
注意如果不足两位小数则填充0保证两位小数 还需要注意new BigDecimal("-0")(无论-0还是0)保留两位小数都是0.00
至于负数情况只是前面加个负号。

舍入策略注意点代码标志对应源码值核心功能特点
第三位小数只要>0 就进1(0 除外)都会进1,负数加-即可ROUND_UP0向上取整
第三位小数直接舍去直接舍去,负数加-即可ROUND_DOWN1向下取整
第三位小数>=5,进行四舍五入负数加-即可ROUND_HALF_UP4常规四舍五入
第三位小数>5,进行五舍六入负数加-即可ROUND_HALF_DOWN5常规五舍六入
如果为正数等价于:ROUND_UP;为负数等价于:ROUND_DOWNROUND_UP、ROUND_DOWN 的搭配正用ROUND_CEILING2此模式会增加数值大小
如果为正数等价于:ROUND_DOWN;为负数等价于:ROUND_UPROUND_UP、ROUND_DOWN 的搭配逆用ROUND_FLOOR3此模式不会增加数值大小
四舍六入五成双四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一ROUND_HALF_EVEN6银行家算法
断言我对目标数保留的位数是否>=目标数的位数无舍入逻辑即不需要舍入,不满足则会报错:ArithmeticExceptionROUND_UNNECESSARY7具备断言(判断)功能

看以上表格我说一嘴:

  1. RoundingMode取值范围0到7 否则报错:java.lang.IllegalArgumentException: Invalid rounding mode
  2. ROUND_HALF_EVEN 一般是业务中最常用的,也是这几种保留策略最复杂的,看完案例其实还是很简单的,技巧是 四舍六入五考虑,五后非零就进一(原数位数比你要保留的位数多至少2位),五后为零看奇偶,五前为偶应舍去,五前为奇要进一
  3. 对表格里面说法不理解请看下面具体每个的案例。
    可参考此内容
    ROUND_UP
    进位制:不管保留数字后面是大是小 (0 除外) 都会进 1。结果会向原点的反方向对齐,
    正数向正无穷方向对齐,负数向负无穷方向对齐。
        BigDecimal a = new BigDecimal("0.090").setScale(2, 0); // 0.09
        BigDecimal a1 = new BigDecimal("0.091").setScale(2, BigDecimal.ROUND_UP); // 0.10
        BigDecimal a2 = new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_UP); // -0.09
        BigDecimal a3 = new BigDecimal("-0.091").setScale(2, BigDecimal.ROUND_UP); // -0.10
        BigDecimal a4 = new BigDecimal("-0.1").setScale(2, BigDecimal.ROUND_UP); // -0.10
        BigDecimal a5 = new BigDecimal("-0").setScale(2, BigDecimal.ROUND_UP); // 0.00

ROUND_DOWN
舍去制,截断操作,后面所有数字直接去除。结果会向原点方向对齐。

        BigDecimal a = new BigDecimal("0.090").setScale(2, 1); // 0.09
        BigDecimal a1 = new BigDecimal("0.091").setScale(2, BigDecimal.ROUND_DOWN); // 0.09
        BigDecimal a2 = new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_DOWN); // -0.09
        BigDecimal a3 = new BigDecimal("-0.091").setScale(2, BigDecimal.ROUND_DOWN); // -0.09
        BigDecimal a4 = new BigDecimal("-0.1").setScale(2, BigDecimal.ROUND_DOWN); // -0.10
        BigDecimal a5 = new BigDecimal("-0").setScale(2, BigDecimal.ROUND_DOWN); // 0.00

ROUND_HALF_UP
根据保留数字后一位 >=5 进行四舍五入。如果舍弃部分的最高位大于等于 5,向原点反方向对齐,否则向原点方向对齐。

        BigDecimal a = new BigDecimal("0.090").setScale(2, 4); // 0.09
        BigDecimal a1 = new BigDecimal("0.091").setScale(2, BigDecimal.ROUND_HALF_UP); // 0.09
        BigDecimal a2 = new BigDecimal("-0.091").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.09
        BigDecimal a3 = new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_HALF_UP); // 0.10
        BigDecimal a4 = new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.10
        BigDecimal a5 = new BigDecimal("-0").setScale(2, BigDecimal.ROUND_HALF_UP); // 0.00

ROUND_HALF_DOWN
根据保留数字后一位 >5 进行五舍六入。如果舍弃部分的最高位大于 5,向原点反方向对齐,否则向原点方向对齐。 这种模式也就是我们常说的 “五舍六入”。

        BigDecimal a = new BigDecimal("0.095").setScale(2, 5); // 0.09
        BigDecimal a1 = new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_HALF_DOWN); // 0.09
        BigDecimal a2 = new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_HALF_DOWN); // -0.09
        BigDecimal a3 = new BigDecimal("0.096").setScale(2, BigDecimal.ROUND_HALF_DOWN); // 0.10
        BigDecimal a4 = new BigDecimal("-0.096").setScale(2, BigDecimal.ROUND_HALF_DOWN); // -0.10
        BigDecimal a5 = new BigDecimal("-0").setScale(2, BigDecimal.ROUND_HALF_DOWN); // 0.00

ROUND_CEILING
向正无穷方向对齐,转换为正无穷方向最接近的数值。如果为正数,行为和 ROUND_UP 一样;
如果为负数,行为和 ROUND_DOWN 一样。此模式不会减少数值大小。

        BigDecimal a = new BigDecimal("0.095").setScale(2, 2); // 0.10
        BigDecimal a1 = new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_CEILING); // 0.10
        BigDecimal a2 = new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_CEILING); // -0.09
        BigDecimal a3 = new BigDecimal("0.096").setScale(2, BigDecimal.ROUND_CEILING); // 0.10
        BigDecimal a4 = new BigDecimal("-0.096").setScale(2, BigDecimal.ROUND_CEILING); // -0.09
        BigDecimal a5 = new BigDecimal("-0").setScale(2, BigDecimal.ROUND_CEILING); // 0.00

ROUND_FLOOR
向负无穷方向对齐。如果为正数,行为和 ROUND_DOWN 一样;
如果为负数,行为和 ROUND_UP 一样。此模式不会增加数值大小。

        BigDecimal a = new BigDecimal("0.095").setScale(2, 3); // 0.09
        BigDecimal a1 = new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_FLOOR); // 0.09
        BigDecimal a2 = new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_FLOOR); // -0.10
        BigDecimal a3 = new BigDecimal("0.096").setScale(2, BigDecimal.ROUND_FLOOR); // 0.09
        BigDecimal a4 = new BigDecimal("-0.096").setScale(2, BigDecimal.ROUND_FLOOR); // -0.10
        BigDecimal a5 = new BigDecimal("-0").setScale(2, BigDecimal.ROUND_FLOOR); // 0.00

ROUND_HALF_EVEN
技巧:>6或=5且前一位为奇数进1,4>或=5且前一位为偶数舍去
四舍六入五成双,如果舍弃部分的最高位大于等于六,或等于五并且前一位是奇数,向原点反方向对齐,否则向原点方向对齐。
如果舍弃部分左边的数字为奇数,则作 ROUND_HALF_UP;如果为偶数,则作ROUND_HALF_DOWN。
ROUND_HALF_UP 根据保留数字后一位 >=5 进行四舍五入。
ROUND_HALF_DOWN 根据保留数字后一位 >5 进行五舍六入。

四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。

        BigDecimal a = new BigDecimal("0.035").setScale(2, 6); // 0.04
        BigDecimal a1 = new BigDecimal("0.045").setScale(2, BigDecimal.ROUND_HALF_EVEN); // 0.04
        BigDecimal a2 = new BigDecimal("0.055").setScale(2, BigDecimal.ROUND_HALF_EVEN); // 0.06
        BigDecimal a3 = new BigDecimal("0.065").setScale(2, BigDecimal.ROUND_HALF_EVEN); // 0.06
        BigDecimal a4 = new BigDecimal("0.075").setScale(2, BigDecimal.ROUND_HALF_EVEN); // 0.08
        BigDecimal a5 = new BigDecimal("-0.035").setScale(2, 6); // -0.04
        BigDecimal a6 = new BigDecimal("-0.045").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.04
        BigDecimal a7 = new BigDecimal("-0.055").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.06
        BigDecimal a8 = new BigDecimal("-0.065").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.06
        BigDecimal a9 = new BigDecimal("-0.075").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.08
        BigDecimal a10 = new BigDecimal("-0").setScale(2, BigDecimal.ROUND_HALF_EVEN); // 0.00
        BigDecimal a11 = new BigDecimal("-0.0851").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.09

ROUND_UNNECESSARY
注意点
1、无舍入逻辑即不需要舍入,
2、也就是作用 断言我对目标数保留的位数是否>=目标数的位数
断言请求的操作具有精确的结果,因此不需要舍入。
如果对获得非精确结果的操作指定此舍入模式,则抛出 ArithmeticException。

        //Exception in thread "main" java.lang.ArithmeticException: Rounding necessary
        //BigDecimal a = new BigDecimal("0.035").setScale(2,  BigDecimal.ROUND_UNNECESSARY);
        BigDecimal a1 = new BigDecimal("0.03").setScale(2, BigDecimal.ROUND_UNNECESSARY);//0.03
        BigDecimal a2 = new BigDecimal("0.3").setScale(2, BigDecimal.ROUND_UNNECESSARY);//0.30
        BigDecimal a3 = new BigDecimal("300").setScale(2, BigDecimal.ROUND_UNNECESSARY);//300.00
        BigDecimal a5 = new BigDecimal("-0.03").setScale(2, BigDecimal.ROUND_UNNECESSARY);//-0.03
        BigDecimal a6 = new BigDecimal("-0").setScale(2, BigDecimal.ROUND_UNNECESSARY); // 0.00

==RoundingMode取值范围0到7 ==
否则,java.lang.IllegalArgumentException: Invalid rounding mode

//BigDecimal a9 = new BigDecimal("-0").setScale(2, 8);//IllegalArgumentException

提一下银行家算法

在这里插入图片描述
在这里插入图片描述

BigDecimal和BigInteger

一个针对浮点一个针对整形操作。
当对数字精度要求比较高时使用,BigInteger是对比Long范围还大的数字操作,而BigDecimal是比double还要精确的数字操作
主要有加减乘除方法:add()相加, subtract()相减, multiply()相乘, divide()相除。
顺便说一下,java.util包中的Scanner类实现了nextBigInteger()和nextBigDecimal()方法,可以用来读入控制台输入的BigInteger和BigDecimal。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

BigInteger i1 = new BigInteger("79846513898465139846513289653");
BigInteger i2 = new BigInteger("46513986539856298465398465132");
//add()相加,  subtract()相减,  multiply()相乘, divide()相除
BigInteger i3 = i1.add(i2);
System.out.println( i3 );
System.out.println( i1.subtract(i2));
System.out.println( i1.multiply(i2));
System.out.println( i1.divide(i2));

//小数有可能会出现除不断的情况
BigDecimal d1 = new BigDecimal("79846597846513984653978465.985");
BigDecimal d2 = new BigDecimal("465132645132978465.985");
//小数相除,如果除不断就会产生java.lang.ArithmeticException算术异常
//小数相除时,经常指定小数的位数(这里保留10位小数), 及尾数的处理方式

System.out.println( d1.divide(d2, 10, RoundingMode.FLOOR));
//RoundingMode是一种枚举类型, 枚举可以看作是一组常量的组合
//枚举也是一种引用数据类型, 可以定义变量, 赋值时只能赋值枚举类型中定义的常量值
RoundingMode  mode = RoundingMode. ROUND_HALF_UP;// 表示的就是4舍5入

下面说点不重要的东西,多知道一点
在这里插入图片描述
numBits范围非负数即>=0
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

        System.out.println("构造两个BigInteger对象");
        BigInteger bi1 = new BigInteger(55,new Random());
        BigInteger bi2 = new BigInteger(new byte[]{3,2,3});
        System.out.println("bi1 + bi2 = " + bi1.add(bi2));  //加
        System.out.println("bi1 - bi2 = " + bi1.subtract(bi2));    //减
        System.out.println("bi1 * bi2 = " + bi1.multiply(bi2));  //乘
        System.out.println("bi1的2次方 = " + bi1.pow(2)); //指数运算
        //因为是整形我就不存在小数,小数根据舍入规则舍弃了
        System.out.println("bi1/bi2的整数商: " + bi1.divide(bi2));  //整数商
        System.out.println("bi1/bi2的余数: " + bi1.remainder(bi2));//余数
        //todo  整数商+余数
        System.out.println("bi1 / bi2 = " + bi1.divideAndRemainder(bi2)[0] +
                "--" + bi1.divideAndRemainder(bi2)[1]);
        System.out.println("bi1 + bi2 = " + bi1.add(bi2));
        //比较大小,也可以用max()和min() 
        if(bi1.compareTo(bi2) > 0){
            System.out.println("bi1 is greater than bi2");
        }
        else if(bi1.compareTo(bi2) == 0){
            System.out.println("bi1 is equal to bi2");
        }else if(bi1.compareTo(bi2) < 0){
            System.out.println("bd1 is lower than bd2");
        }
        BigInteger bi3 = bi1.negate();
        System.out.println("bi1的相反数:" + bi3);   //返回相反数
        System.out.println("bi1的绝对值:" + bi3.abs());//返回绝对值
       

转换进制


       String string1 = new BigInteger("20", 10).toString(2);
       System.out.println("TAG 十进制的20转换成二进制是:" + string1);

        String string2 = new BigInteger("20", 10).toString(8);
       System.out.println("TAG 十进制的20转换成八进制是:" + string2);

        String string3 = new BigInteger("20", 10).toString(16);
       System.out.println("TAG 十进制的20转换成十六进制是:" + string3);

        String string4 = new BigInteger("110", 2).toString(10);
       System.out.println("TAG 二进制的110转换成十进制是:" + string4);

        String string5 = new BigInteger("110", 8).toString(10);
       System.out.println("TAG 八进制的110转换成十进制是:" + string5);

        String string6 = new BigInteger("110", 16).toString(10);
       System.out.println("TAG 十六进制的110转换成十进制是:" + string6);

Java中BigDecimal常用方法

在这里插入图片描述

在这里插入图片描述
来点不一样的玩法
在这里插入图片描述
在这里插入图片描述



        System.out.println("构造两个BigDecimal对象");
        //用char[]数组创建BigDecimal对象,第二个参数为位移offset, 第三个参数指定长度
        BigDecimal bd1 = new BigDecimal("3464656776868432998434".toCharArray(), 2, 15);
        //用double类型创建BigDecimal对象 下面肯定会精度丢失
        BigDecimal bd2 = new BigDecimal(134258767575867.0F);
        System.out.println("bd1 + bd2 = " + bd1.add(bd2)); //加
        System.out.println("bd1 - bd2 = " + bd1.subtract(bd2));//减
        System.out.println("bd1 * bd2 = " + bd1.multiply(bd2));//乘
        System.out.println("bd1的2次方 = " + bd1.pow(2));//指数运算
        //todo 取商的整数部分
        System.out.println("bd1/bd2的整数商: " + bd1.divideToIntegralValue(bd2));
//        返回余数计算为:this.subtract(this.divideToIntegralValue(divisor).multiply(divisor))
        System.out.println("bd1/bd2的余数: " + bd1.subtract(bd1.divideToIntegralValue(bd2).multiply(bd2)));
        System.out.println("bd1/bd2的余数: " + bd1.remainder(bd2));
        //取商和余,即bd1.divideToIntegralValue(bd2)与bd1.remainder(bd2)
        System.out.println("bd1 / bd2 = " + bd1.divideAndRemainder(bd2)[0] +
                "--" + bd1.divideAndRemainder(bd2)[1]);
        //比较大小,也可以用max()和min()
        if (bd1.compareTo(bd2) > 0) {
            System.out.println("bd1 is greater than bd2");
        } else if (bd1.compareTo(bd2) == 0) {
            System.out.println("bd1 is equal to bd2");
        } else if (bd1.compareTo(bd2) < 0) {
            System.out.println("bd1 is lower than bd2");
        }
        //末位数据精度 整数为1 小数为其小数点精度
        //返回该BigDecimal的最后一个单位ulp的大小。非零BigDecimal值的ulp是该值与具有相同位数的下一个较大的BigDecimal值之间的正距离。
        // ulp为0时,在这个比例下数值等于1。结果以相同的比例存储,因此0和非0值的结果等于[1,this.scale()]。
        System.out.println("bd1的末位数据精度:  " + bd1.ulp());
        System.out.println("末位数据精度:  " + new BigDecimal("123.21").ulp());//0.01
        System.out.println("末位数据精度:  " + new BigDecimal("123.21134").ulp());//0.00001
        System.out.println("末位数据精度:  " + new BigDecimal("123").ulp());//1
        System.out.println("末位数据精度:  " + new BigDecimal("0").ulp());//1

为什么说总是使用compareTo()比较两个BigDecimal的值,不要使用equals()!
BigDecimal d1 = new BigDecimal(“123.456”);
在这里插入图片描述

操作数据工具类,

比如±*/的基本操作,还有,相反数,最大公约数,是否是质数等等的运算。
BigDecimal转BigInteger
获取小数位数、小数精度,绝对值、不绝对值、取反、去除小数点后无用0、
取商的整数部分、取余数、返回商跟余数、
返回一个BigDecimal,设置指定保留位数,及舍入策略。
返回BigDecimal和val的最大值、最小值。
向左向右移动小数点的方法、判断一个数是正数、0、还是负数

 /**
     * 获取小数位数
     */
    public static int getScale(BigDecimal b) {
        return b.scale();
    }
    //上面的升级版,去掉后面的0
    public static int getScale2(BigDecimal b) {
        return  b.stripTrailingZeros().scale();
    }
    /**
     * 获取小数精度 大于6位小数后采用科学计数法
     * 末位数据精度 整数为1 小数为其小数点精度
     */
    public static BigDecimal getUlpDecimal(BigDecimal b) {
        return b.ulp();
    }
    //去除末尾无用的0后的小数精度
    public static BigDecimal getUlpDecimal2(BigDecimal b) {
        return  b.stripTrailingZeros().ulp();
    }

    /**
     * 方法返回此BigDecimal的精度。精度是指非标度值中的位数。
     * 这个不会科学计数,但是返回值跟上面不一样,
     * 此方法返回一个整数,该整数表示此BigDecimal对象的精度。
     * Input : 198.176 Output : 6 
     * Input : 721111.111 Output : 9
     * Input : 0.3 Output : 1
     * 
     */
    public static int precisionBigDecimal(BigDecimal bd){
        return bd.precision();
    }

    public static int precisionBigDecimal2(BigDecimal bd){
        return bd.stripTrailingZeros().precision();
    }

    /**
     * 获取绝对值---负数转正数
     */
    public static BigDecimal getABSBigDecimal(BigDecimal b) {
        return b.abs();
    }

    /**
     * 跟绝对值反着来---正数转负数
     */
    public static BigDecimal getNotABSBigDecimal(BigDecimal b) {
        BigDecimal abs = b.abs();
        return new BigDecimal("-" + b.abs());
    }

    /**
     * 返回相反数---正数转负数,负数转正数(作用就是负数去负号,正数加负号)
     * 如果你需要进行取舍的话他还支持 传参MathContext.  具体规则自行百度
     */
    public static BigDecimal getNegateBigDecimal(BigDecimal b) {
        return b.negate();
    }

    /**
     * 取商的整数部分(直接把小数舍去)
     * bd1/bd2的整数商
     */
    public static BigDecimal getDivShang(BigDecimal bd1, BigDecimal bd2) {
        return bd1.divideToIntegralValue(bd2);
    }

    /**
     * 返回余数计算 (直接把小数舍去)
     * bd1/bd2的整数商
     */
    public static BigDecimal getDivMod(BigDecimal bd1, BigDecimal bd2) {
        return bd1.remainder(bd2);
//        return  bd1.subtract(bd1.divideToIntegralValue(bd2).multiply(bd2));
    }

    /**
     * 取商和余,即bd1.divideToIntegralValue(bd2)与bd1.remainder(bd2)
     */
    public static String getDivShangAndMod(BigDecimal bd1, BigDecimal bd2) {
        return bd1.divideAndRemainder(bd2)[0] + "余" + bd1.divideAndRemainder(bd2)[1];
    }

    /**
     * 返回一个BigDecimal,它在数值上与此值相等,但从表示形式中移除尾随的任何零。
     */
    public static BigDecimal getTrailingZeros(BigDecimal bd1) {
        return bd1.stripTrailingZeros();
    }

    /**
     * 返回一个BigDecimal,设置指定保留位数,及舍入策略
     */
    public static BigDecimal getTargetScale(BigDecimal bd1,int numWei,int ceLue) {
        return bd1.setScale(numWei,ceLue);
    }

    /**
     * 返回BigDecimal和val的最大值,相等返回bd1
     */
    public static BigDecimal getMaxBigDecimal(BigDecimal bd1,BigDecimal bd2) {
        return bd1.max(bd2);
    }
    /**
     * 返回BigDecimal和val的最小值,相等返回bd1
     */
    public static BigDecimal getMinBigDecimal(BigDecimal bd1,BigDecimal bd2) {
        return bd1.min(bd2);
    }

/**
 * 将该值的小数点移动n位到左边。如果n为非负,则调用仅将n添加至刻度。如果n为负时,调用相当于调用movePointRight(-n)。
 */
public static BigDecimal movePointLeftBigDecimal(BigDecimal bd,int num){
    return bd.movePointLeft(num);
}
    public static BigDecimal movePointRightBigDecimal(BigDecimal bd,int num){
        return bd.movePointRight(num);
    }

    /**
     * 看你是正数负数还是0
     * 基于此BigDecimal返回这三个值[-1、1或0]中的任何一个
     */
    public static int signumBigDecimal(BigDecimal bd){
        return bd.signum();
    }

    /**
     * BigDecimal转BigInteger
     */
    public static BigInteger BigDecimalToBigInteger(BigDecimal decimal) {
        return decimal.toBigInteger();
    }

    /**
     * 最大公约数
     * @return
     */
    public static BigInteger bigIntegerGcd(BigInteger bigInteger1, BigInteger bigInteger2) {
        return bigInteger1.gcd(bigInteger2);
    }

/**
 * 如果BigInteger可能是质数,则返回true,如果绝对是合数则返回false。如果确定性≤0,则返回true
 */
public static boolean BigIntegerIsProbablePrime(BigInteger integer) {
    return integer.isProbablePrime(2);
}

在这里插入图片描述



import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;

/**
 * BigDecimal、Double、String之间的互转
 * 加减乘除的方法,都是重载方法,根据需求可扩容仿写即可(注意这块默认四舍五入策略,具体根需求修改)
 * 提供BigDecimal、Double、String之间的精确的小数位四舍五入处理
 * 两个数相除取余数
 * 两个数比较大小
 * <p>
 * <p>
 * 把阿拉伯数字转换为罗马数字:intToRomanNum
 * 把罗马数字转换为阿拉伯数字:romanNumToInt
 * int 转 中文数字:intTochineseNum(缺陷数字只能到亿)
 * bigDecimal(包含小数点) 转中文数字  bigDecimalTochineseNum(缺陷整数数字只能到亿)
 * 将任意长度的字符串str转换为中国传统金额表示方式:convertChineseNum (缺陷数字不支持小数)
 * 及正常金额 convertHanNum (缺陷数字不支持小数)
 * <p>
 *     下面方法在使用时候先去看一下方法,返回特点是不是想要的
 * BigDecimal货币格式化 currencyFormat
 * BigDecimal百分比格式化 rateFormat
 */
public class BigDecimalArithmeticUtils {

    private BigDecimalArithmeticUtils() {
    }

    //默认除法运算精度  保留10位小数
    private static final int DEF_DIV_SCALE = 10;

    /**
     * BigDecimal转Double
     *
     * @param bigDecimal
     * @return
     */
    public static double BigDecimalToDouble(BigDecimal bigDecimal) {
        return bigDecimal.doubleValue();
    }

    /**
     * BigDecimal转String
     *
     * @param bigDecimal
     * @return
     */
    public static String BigDecimalToString(BigDecimal bigDecimal) {
        return bigDecimal.toString();
    }

    /**
     * Double转BigDecimal
     *
     * @param v
     * @return
     */
    public static BigDecimal DoubleToBigDecimal(double v) {
        return new BigDecimal(Double.toString(v));
    }

    /**
     * Double转String
     *
     * @param v
     * @return
     */
    public static String DoubleToString(double v) {
        return Double.toString(v);
    }

    /**
     * 提供精确的加法运算
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */

    public static double addReturnDouble(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }

    public static BigDecimal add(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2);
    }

    /**
     * 提供精确的加法运算
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2);
    }


    /**
     * 提供精确的加法运算
     *
     * @param v1    被加数
     * @param v2    加数
     * @param scale 保留scale 位小数
     * @return 两个参数的和
     */
    public static String add(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 精确的加法运算,并对运算结果截位.
     */
    public static BigDecimal add(BigDecimal b1, BigDecimal b2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    public static String add(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 提供精确的减法运算
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static double subReturnDouble(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }

    public static BigDecimal sub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2);
    }

    /**
     * 提供精确的减法运算。
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static BigDecimal sub(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2);
    }

    /**
     * 提供精确的减法运算
     *
     * @param v1    被减数
     * @param v2    减数
     * @param scale 保留scale 位小数
     * @return 两个参数的差
     */
    public static String sub(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    public static String sub(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    public static BigDecimal sub(BigDecimal v1, BigDecimal v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        return v1.subtract(v2).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static double mulReturnDouble(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }

    public static BigDecimal mul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2);
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal mul(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2);
    }

    public static BigDecimal mul(BigDecimal v1, BigDecimal v2) {
        return v1.multiply(v2);
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1    被乘数
     * @param v2    乘数
     * @param scale 保留scale 位小数
     * @return 两个参数的积
     */
    public static double mul(double v1, double v2, int scale) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return round(b1.multiply(b2).doubleValue(), scale);
    }


    /**
     * 提供精确的乘法运算
     *
     * @param v1    被乘数
     * @param v2    乘数
     * @param scale 保留scale 位小数
     * @return 两个参数的积
     */
    public static String mul(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    public static BigDecimal mul(BigDecimal v1, BigDecimal v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        return v1.multiply(v2).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
     * 小数点以后10位,以后的数字四舍五入
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */

    public static double div(double v1, double v2) {
        return divReturnDouble(v1, v2, DEF_DIV_SCALE);
    }

    /**
     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
     * 定精度,以后的数字四舍五入
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static double divReturnDouble(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    public static BigDecimal divide(BigDecimal b1, BigDecimal b2) {
        return b1.divide(b2);
    }

    public static BigDecimal div(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP);
    }

    public static BigDecimal div(BigDecimal v1, BigDecimal v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        return v1.divide(v2, scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
     * 定精度,以后的数字四舍五入
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示需要精确到小数点以后几位
     * @return 两个参数的商
     */
    public static String div(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v1);
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 提供精确的小数位四舍五入处理
     *
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    public static BigDecimal round(BigDecimal v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        return v.setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 提供精确的小数位四舍五入处理
     *
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static String round(String v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(v);
        return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }


    /**
     * 取余数
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 小数点后保留几位
     * @return 余数
     */
    public static String remainder(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    public static String remainder(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 取余数  BigDecimal
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 小数点后保留几位
     * @return 余数
     */
    public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 比较大小
     *
     * @param v1 被比较数
     * @param v2 比较数
     * @return 如果v1 大于v2 则 返回true 否则false
     */

    public static boolean compare(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        int bj = b1.compareTo(b2);
        boolean res;
        if (bj > 0)
            res = true;
        else
            res = false;
        return res;
    }

    public static int compareInt(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.compareTo(b2);

    }


    public static boolean compare(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        int bj = b1.compareTo(b2);
        boolean res;
        if (bj > 0)
            res = true;
        else
            res = false;
        return res;
    }

    public static int compareInt(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.compareTo(b2);
    }


    public static boolean compare(BigDecimal v1, BigDecimal v2) {
        int bj = v1.compareTo(v2);
        boolean res;
        if (bj > 0)
            res = true;
        else
            res = false;
        return res;
    }

    //比较BigDecimal,相等返回0,v1>v2 返回1, v1<v2 返回-1
    public static int compareInt(BigDecimal v1, BigDecimal v2) {
        return v1.compareTo(v2);

    }

    //建立货币格式化引用
    private static final NumberFormat currency = NumberFormat.getCurrencyInstance();


    /**
     * "10042356.325354" ===>  ¥10,042,356.33    100456 ===>  ¥100,456.00
     * BigDecimal货币格式化 currencyFormat
     *
     * @param money
     * @return
     */
    public static String currencyFormat(BigDecimal money) {
        return currency.format(money);
    }




    /**
     * 将数字转换成带逗号的字符串("###,##0.## ")
     * @param reg 格式化串
     * @param number 需要转换的数字
     * @return
     */
    public static String numberFormart(String reg,double number){
        DecimalFormat bf = new DecimalFormat(reg);
        return bf.format(number);
    }

    //建立百分比格式化引用
    private static final NumberFormat percent = NumberFormat.getPercentInstance();

    /**
     * BigDecimal百分比格式化
     * 0.12 ====>12%  1.4 ===>140%
     * 0.005 ==> 0%   0.015==>2%    0.1445==>14%
     * @param rate
     * @return
     */
    public static String rateFormat(BigDecimal rate) {
        return percent.format(rate);
    }

    /**
     * double类型的数字转化为百分之
     *
     * @param doubleNum
     *            double类型
     * @return eg:类似21%的百分之
     */
    public static String double2Percent(double doubleNum) {
        DecimalFormat df = new DecimalFormat("##.00%");
        if (doubleNum == 0.0)
            return "0%";
        return df.format(doubleNum);
    }

    public static String double2Percent(BigDecimal doubleNum) {
        DecimalFormat df = new DecimalFormat("##.00%");
//        if (doubleNum == 0.0)
//            return "0%";
        return df.format(doubleNum);
    }

    public static String double2Percent(String doubleNum) {
        DecimalFormat df = new DecimalFormat("##.00%");
//        if (doubleNum == 0.0)
//            return "0%";
        return df.format(doubleNum);
    }

    /**
     * 把阿拉伯数字转换为罗马数字  intToRomanNum
     * 罗马数字是古罗马使用的数字系统,现今仍很常见。罗马数字共有7个,
     * 即Ⅰ(1),Ⅴ(5),Ⅹ(10),Ⅼ(50),Ⅽ(100),Ⅾ(500),Ⅿ(1000)。
     * 需要注意的是罗马数字中没有“0”。
     *
     * @param number
     * @return 小于1大于3999 返回-1
     */
    public static String intToRomanNum(int number) {
        String rNumber = "";
        int[] aArray = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        String[] rArray = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X",
                "IX", "V", "IV", "I"};
        if (number < 1 || number > 3999) {
            rNumber = "-1";
        } else {
            for (int i = 0; i < aArray.length; i++) {
                while (number >= aArray[i]) {
                    rNumber += rArray[i];
                    number -= aArray[i];
                }
            }
        }
        return rNumber;
    }

    /**
     * 把罗马数字转换为阿拉伯数字
     *
     * @param m
     * @return
     */
    public static int romanNumToInt(String m) {
        int graph[] = new int[400];
        graph['I'] = 1;
        graph['V'] = 5;
        graph['X'] = 10;
        graph['L'] = 50;
        graph['C'] = 100;
        graph['D'] = 500;
        graph['M'] = 1000;
        char[] num = m.toCharArray();
        int sum = graph[num[0]];
        for (int i = 0; i < num.length - 1; i++) {
            if (graph[num[i]] >= graph[num[i + 1]]) {
                sum += graph[num[i + 1]];
            } else {
                sum = sum + graph[num[i + 1]] - 2 * graph[num[i]];
            }
        }
        return sum;
    }


    /**
     * 中文数字
     */
    private static final String[] CN_NUM = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};

    /**
     * 中文数字单位
     */
    private static final String[] CN_UNIT = {"", "十", "百", "千", "万", "十", "百", "千", "亿", "十", "百", "千"};

    /**
     * 特殊字符:负
     */
    private static final String CN_NEGATIVE = "负";

    /**
     * 特殊字符:点
     */
    private static final String CN_POINT = "点";

    /**
     * int 转 中文数字
     * 支持到int最大值
     *
     * @param intNum 要转换的整型数
     * @return 中文数字
     */
    public static String intTochineseNum(int intNum) {
        StringBuffer sb = new StringBuffer();
        StringBuffer sb2 = new StringBuffer();
        int intNum2 = intNum;
        boolean isNegative = false;
        if (intNum < 0) {
            isNegative = true;
            intNum *= -1;
        }
        int count = 0;
        while (intNum > 0) {
            sb.insert(0, CN_NUM[intNum % 10] + CN_UNIT[count]);
            intNum = intNum / 10;
            count++;
        }

        if (isNegative)
            sb.insert(0, CN_NEGATIVE);
        // 10-19时,得到十~十九而不是一十~一十九
        sb = "一".equals(sb.substring(0, 1)) && intNum2 < 100 && intNum2 > 1 ? sb2.append(sb.substring(1, sb.length())) : sb;
        return sb.toString().replaceAll("零[千百十]", "零").replaceAll("零+万", "万")
                .replaceAll("零+亿", "亿").replaceAll("亿万", "亿零")
                .replaceAll("零+", "零").replaceAll("零$", "");
    }


    /**
     * bigDecimal 转 中文数字  bigDecimalTochineseNum
     * 整数部分只支持到int的最大值
     *
     * @param bigDecimalNum 要转换的BigDecimal数
     * @return 中文数字
     */
    public static String bigDecimalTochineseNum(BigDecimal bigDecimalNum) {
        if (bigDecimalNum == null)
            return CN_NUM[0];

        StringBuffer sb = new StringBuffer();

        //将小数点后面的零给去除
        String numStr = bigDecimalNum.abs().stripTrailingZeros().toPlainString();

        String[] split = numStr.split("\\.");
        String integerStr = intTochineseNum(Integer.parseInt(split[0]));

        sb.append(integerStr);

        //如果传入的数有小数,则进行切割,将整数与小数部分分离
        if (split.length == 2) {
            //有小数部分
            sb.append(CN_POINT);
            String decimalStr = split[1];
            char[] chars = decimalStr.toCharArray();
            for (int i = 0; i < chars.length; i++) {
                int index = Integer.parseInt(String.valueOf(chars[i]));
                sb.append(CN_NUM[index]);
            }
        }

        //判断传入数字为正数还是负数
        int signum = bigDecimalNum.signum();
        if (signum == -1) {
            sb.insert(0, CN_NEGATIVE);
        }

        return sb.toString();
    }

    /**
     * 将任意长度的字符串str转换为中国传统金额表示方式,打印到控制台上
     * 金额转换,阿拉伯数字转换成中国传统形式。
     * 例如:101000001010   转换为   壹仟零壹拾亿零壹仟零壹拾圆整
     * 注意:00101000000010等价101000000010 去除非法零
     * 本题实现比较复杂,主要是对连续的零的判断,通过循环判断每一位及前一位是否为零,来控制是否删除多余的零。
     * 实现了对任意长度的字符串的金额转换。
     */
    public static String convertChineseNum(String str) {
        //使用正则表达式判断字符串格式是否为数字格式
        if (!str.matches("\\d+")) {
            return "字符串" + str + "不是数字格式,无法转换!";
        }
        //将字符串最前面的0去掉
        while (str.startsWith("0")) {
            str = str.substring(1, str.length());
        }
        //将字符串数字替换成中文数字
        char[] num = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
        char[] cnNum = new char[]{'零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'};
        for (int i = 0; i < 10; i++) {
            str = str.replace(num[i], cnNum[i]);
        }

        StringBuilder sb = new StringBuilder(str);//StringBuilder存放字符串,用于插入删除操作
        int index = str.length() - 1;   //从第index个字符从后往前开始操作,操作一次,index--
        //存放金额单位
        String[] unit = {"", "拾", "佰", "仟"};
        String[] unit4 = {"万", "亿"};

        boolean flag = false;//判断前一个数是否为零的标记

        for (int i = 0; i < str.length(); i++) {//循环体内对sb进行操作

            //每4位插入万每8位插入亿,最低位先不设置单位
            if (i % 4 == 0 && i != 0) {
                //根据金额规则,单位万前面为4个零时,不插入万,并将index位置(最低位)的零删除
                if ((i / 4 - 1) % 2 == 0 && index >= 3 && str.substring(index - 3, index + 1).equals("零零零零")) {
                    sb.deleteCharAt(index);
                    index--;
                    continue;
                }
                //否则在index+1位置插入相应的单位:万、亿
                sb.insert(index + 1, unit4[(i / 4 - 1) % 2]);
            }
            //如果4位的最低位为零,删除最低位的零,将零标志置为true
            if (i % 4 == 0 && str.charAt(index) == '零') {
                sb.deleteCharAt(index);
                flag = true;
                index--;
                continue;
            }
            //如果前一位为零并且这一位也为零,删除这一位的零
            if (flag && str.charAt(index) == '零') {
                sb.deleteCharAt(index);
                index--;
                continue;
            }
            //如果当前位为零,将零标志置为true
            if (str.charAt(index) == '零') {
                flag = true;
                index--;
                continue;
            }
            //当前位不为零,将零标志位置为false,插入金额单位
            flag = false;
            sb.insert(index + 1, unit[i % 4]);
            index--;
        }
        //完善金额表示形式
        sb.append("圆整");
        return sb.toString();
    }

    public static String convertHanNum(String str) {
        //使用正则表达式判断字符串格式是否为数字格式
        if (!str.matches("\\d+")) {
            return "字符串" + str + "不是数字格式,无法转换!";
        }
        //将字符串最前面的0去掉
        while (str.startsWith("0")) {
            str = str.substring(1, str.length());
        }
        //将字符串数字替换成中文数字
        char[] num = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
        char[] cnNum = new char[]{'零', '一', '二', '三', '四', '五', '六', '七', '八', '九'};
        for (int i = 0; i < 10; i++) {
            str = str.replace(num[i], cnNum[i]);
        }

        StringBuilder sb = new StringBuilder(str);//StringBuilder存放字符串,用于插入删除操作
        int index = str.length() - 1;   //从第index个字符从后往前开始操作,操作一次,index--
        //存放金额单位
        String[] unit = {"", "十", "百", "千"};
        String[] unit4 = {"万", "亿"};

        boolean flag = false;//判断前一个数是否为零的标记

        for (int i = 0; i < str.length(); i++) {//循环体内对sb进行操作

            //每4位插入万每8位插入亿,最低位先不设置单位
            if (i % 4 == 0 && i != 0) {
                //根据金额规则,单位万前面为4个零时,不插入万,并将index位置(最低位)的零删除
                if ((i / 4 - 1) % 2 == 0 && index >= 3 && str.substring(index - 3, index + 1).equals("零零零零")) {
                    sb.deleteCharAt(index);
                    index--;
                    continue;
                }
                //否则在index+1位置插入相应的单位:万、亿
                sb.insert(index + 1, unit4[(i / 4 - 1) % 2]);
            }
            //如果4位的最低位为零,删除最低位的零,将零标志置为true
            if (i % 4 == 0 && str.charAt(index) == '零') {
                sb.deleteCharAt(index);
                flag = true;
                index--;
                continue;
            }
            //如果前一位为零并且这一位也为零,删除这一位的零
            if (flag && str.charAt(index) == '零') {
                sb.deleteCharAt(index);
                index--;
                continue;
            }
            //如果当前位为零,将零标志置为true
            if (str.charAt(index) == '零') {
                flag = true;
                index--;
                continue;
            }
            //当前位不为零,将零标志位置为false,插入金额单位
            flag = false;
            sb.insert(index + 1, unit[i % 4]);
            index--;
        }
        return sb.toString();
    }
}

BigDecimal的toString()方法和toPlainString()方法

今天遇到个问题 税率竟然采取了科学计数法,该数据类型是BigDecimal 通过 toString转成String类型的。后来换成toPlainString()方法进行解决的,下面分析一下
在这里插入图片描述
toString方法会将BigDecimal的值以科学计数方式的字符串

但是转换成科学计数的方式也是有场景的,并不是所有的值都会转为科学计数方式的字符串。
首先,任何一个BigDecimal都可以使用一个公式表示: unscaledValue × 10-scale

unscaledValue :整数非标度值 (即去掉小数点的Bigdecimal的值,类型为BigInteger)
scale:标度值,如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以 10 的负 scale 次幂。

在这里插入图片描述
在这里插入图片描述

toPlainString()方法是一个非静态方法,仅可通过类对象访问,如果尝试使用类名访问该方法,则会收到错误消息。原值输出为字符串

        String str1 = "12E+5";
        String str2 = "1200000";
        BigDecimal b_dec1 = new BigDecimal(str1);
        BigDecimal b_dec2 = new BigDecimal(str2);
        String str_conv = b_dec1.toPlainString();//1200000
        str_conv = b_dec2.toPlainString();//1200000


        String str3 = "12E+5";
        String str4 = "-12000000";
        BigDecimal b_dec1 = new BigDecimal(str3);
        BigDecimal b_dec2 = new BigDecimal(str4);
        //scale: -5  unscaledValue:12  toString:1.2E+6
        System.out.println("scale: "+b_dec1.scale()+"  unscaledValue:"+b_dec1.unscaledValue()+"  toString:"+b_dec1.toString());
       //scale: 0  unscaledValue:-12000000  toPlainString:-12000000
        System.out.println("scale: "+b_dec2.scale()+"  unscaledValue:"+b_dec2.unscaledValue()+"  toPlainString:"+b_dec2.toString());

当我们使用BigDecimal转成字符串时,大多其实是想单纯的使用toString方法,而并不想使用科学计数的方式,所以应该使用的是toPlainString方法

.stripTrailingZeros()会造成科学计数,也是我工作实际中遇到的,

        //一般直接使用 BigDecimal.toString()方法即可以完成浮点数的打印。
        System.out.println(new BigDecimal("10000000000").toString());//10000000000
        //但是,toString()方法输出的字符串并不能保证不是科学计数法。
        //不过在日常的使用中,用toString()方法输出的就是普通的数字字符串而非科学计数法。
        System.out.println( new BigDecimal("100.000").toString());//程序的输出即为:  100.000
        // 如果我们希望去除末尾多余的0,那么我们应该这么写:
        System.out.println( new BigDecimal("100.000").stripTrailingZeros().toString());
        //其中,stripTrailingZeros()函数就是用于去除末尾多余的0的,但是此时程序的输出为: 1E+2
       // 是科学计数法,可能并不是我们想要的。
        //解决的方法很简单,如果想要避免输出科学计数法的字符串,我们要用toPlainString()函数代替toString()。如:
        System.out.println( new BigDecimal("100.000").stripTrailingZeros().toPlainString());


BigDecimal 返回前端保留小数点后两位问题解决

现象就是,BigDecimal类型的字段,我已经格式化保留两位小数了,
我通过DeBug看返回的也是带小数的 比如 100.00 但是到了前端页面 看返回的确实100
后端返回 decimal 类型 100.00 到前端 会变成100 如何解决呢?
加注解:@JsonFormat(shape = JsonFormat.Shape.STRING)
备注:注解可以加在属性上或get方法上(我是加在属性上了,亲测可用)
如果你只是加在字段属性值上,那你返回给前端的时候需要 重新set该字段,给它格式化你想要的保留位数即可,
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值