一、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、总结
- 在需要精确的小数计算时再使用BigDecimal,BigDecimal的性能比double和float差,在处理庞大,复杂的运算时尤为明显。故一般精度的计算没必要使用BigDecimal。
- 尽量使用参数类型为String的构造函数。
- BigDecimal都是不可变的(immutable)的, 在进行每一次四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得要保存操作后的值。
4.2、工具类推荐
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
五、请关注后续博客
记得点波收藏o!!!