Bigdecimal类相关介绍及常用方法
简述
在一般涉及到具体金额,百分比计算,汇率等需要高精度计算的时候,通常会使用位于java.math包下的Bigdecimal类来进行精准计算,我司主要做航空机票类项目,其中有计算货币的汇率,相关的里程,及会员折扣的业务计算。今天就总结一下Bigdecimal类的API方法与实例使用。
-
构造方法
如图Bigdecimal源码所示,可以看到Bigdecimal有很多类型的构造方法,而我一般常用的是String、int、double、long来创建Bigdecimal实例。初始化为0时,尽量使用Bigdecimal自带常量:BigDecimal.ZERO
BigDecimal bigDecimalString = new BigDecimal("1");
BigDecimal bigDecimalInt = new BigDecimal(1);
BigDecimal bigDecimalDouble = new BigDecimal(1.0);
BigDecimal bigDecimalLong = new BigDecimal(1L);
BigDecimal zero = BigDecimal.ZERO;
-
加减乘除
因为Bigdecimal是实例对象,因此在进行加减乘除运算时不能使用普通的二元运算符来进行操作,而应该调用相关的方法
BigDecimal a = new BigDecimal(2);
BigDecimal b = new BigDecimal(1);
a.add(b);
a.subtract(b);
a.multiply(b);
a.divide(b);
-
舍入规则
当我们在进行除法计算的时候,除数与被除数能够整除时,可以正常使用divide()方法。如果出现不能被整除的情况,则会报算术异常
java.lang.ArithmeticException
打开divide()方法的源码:
MathContext mc = new MathContext( (int)Math.min(this.precision() + (long)Math.ceil(10.0*divisor.precision()/3.0),
Integer.MAX_VALUE),RoundingMode.UNNECESSARY);
在我们没有指定舍入模式时,默认使用了RoundingMode.UNNECESSARY,对其相关的注释为:
Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary. If this rounding mode is specified on an operation that yields an inexact result, an ArithmeticException is thrown.
即如果能确定精确的结果,则不需要指定舍入模式,但若在产生不精确结果的操作上使用RoundingMode.UNNECESSARY,则抛出算术异常。
其余相关舍入模式如下:
RoundingMode.UP
Rounding mode to round away from zero. Always increments the digit prior to a non-zero discarded fraction. Note that this rounding mode never decreases the magnitude of the calculated value. 此模式下为向非舍弃部分增加1,特性为不会减少原有的数值绝对值。
| 输入数字 | 舍入结果 |
|---|---|
| 1.5 | 2 |
| 1.6 | 2 |
| 1.1 | 2 |
| 1.0 | 1 |
| -1.0 | -1 |
| -1.1 | -2 |
| -1.5 | -2 |
RoundingMode.DOWN
Rounding mode to round towards zero. Never increments the digit prior to a discarded fraction (i.e., truncates). Note that this rounding mode never increases the magnitude of the calculated value. 此模式下向舍入部分为向下,可以理解成截断尾数。
| 输入数字 | 舍入结果 |
|---|---|
| 1.5 | 1 |
| 1.6 | 1 |
| 1.1 | 1 |
| 1.0 | 1 |
| -1.0 | -1 |
| -1.1 | -1 |
| -1.5 | -1 |
RoundingMode.CEILING
Rounding mode to round towards positive infinity. If the result is positive, behaves as for RoundingMode.UP; if negative, behaves as for RoundingMode.DOWN. Note that this rounding mode never decreases the calculated value. 在正数的时候,舍入效果类似于RoundingMode.UP,在负数模式下,舍入效果类似于RoundingMode.DOWN。
| 输入数字 | 舍入结果 |
|---|---|
| 1.5 | 2 |
| 1.6 | 2 |
| 1.1 | 2 |
| 1.0 | 1 |
| -1.0 | -1 |
| -1.1 | -1 |
| -1.5 | -1 |
RoundingMode.FLOOR
Rounding mode to round towards negative infinity. If the result is positive, behave as for RoundingMode.DOWN; if negative, behave as for RoundingMode.UP. Note that this rounding mode never increases the calculated value. 在正数的时候,舍入效果类似于RoundingMode.DOWN,在负数模式下,舍入效果类似于RoundingMode.UP。
| 输入数字 | 舍入结果 |
|---|---|
| 1.5 | 1 |
| 1.6 | 1 |
| 1.1 | 1 |
| 1.0 | 1 |
| -1.0 | -1 |
| -1.1 | -2 |
| -1.5 | -2 |
RoundingMode.HALF_UP
Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up. Behaves as for RoundingMode.UP if the discarded fraction is ≥ 0.5; otherwise, behaves as for RoundingMode.DOWN. Note that this is the rounding mode commonly taught at school. 如果舍弃部分>=0.5时,舍入为RoundingMode.UP,否则为RounddingMode.DOWN。这就是我们日常使用的四舍五入了
| 输入数字 | 舍入结果 |
|---|---|
| 1.5 | 2 |
| 1.6 | 2 |
| 1.1 | 1 |
| 1.0 | 1 |
| -1.0 | -1 |
| -1.1 | -1 |
| -1.5 | -2 |
RoundingMode.HALF_UP
Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up. Behaves as for RoundingMode.UP if the discarded fraction is ≥ 0.5; otherwise, behaves as for RoundingMode.DOWN. Note that this is the rounding mode commonly taught at school. 如果舍弃部分>=0.5时,舍入为RoundingMode.UP,否则为RounddingMode.DOWN。这就是我们日常使用的四舍五入了
| 输入数字 | 舍入结果 |
|---|---|
| 1.5 | 2 |
| 1.6 | 2 |
| 1.1 | 1 |
| 1.0 | 1 |
| -1.0 | -1 |
| -1.1 | -1 |
| -1.5 | -2 |
RoundingMode.HALF_DOWN
Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor. Behaves as for RoundingMode.HALF_UP if the digit to the left of the discarded fraction is odd; behaves as for RoundingMode.HALF_DOWN if it's even. Note that this is the rounding mode that statistically minimizes cumulative error when applied repeatedly over a sequence of calculations. It is sometimes known as "Banker's rounding," and is chiefly used in the USA. This rounding mode is analogous to the rounding policy used for float and double arithmetic in Java. 如果舍弃部分>0.5,则等同使用RoundingMode.UP,否则等同使用RoundingMode.DOWN,可以理解为五舍六入
| 输入数字 | 舍入结果 |
|---|---|
| 1.5 | 1 |
| 1.6 | 2 |
| 1.1 | 1 |
| 1.0 | 1 |
| -1.0 | -1 |
| -1.5 | -1 |
| -1.6 | -2 |
RoundingMode.HALF_EVEN
Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary. If this rounding mode is specified on an operation that yields an inexact result, an ArithmeticException is thrown 以5为中点,向最接近数字方向舍入,如果刚好位于5,则判断5后面还有没有数字,如果非0,则入1,如果为0,则判断5的前一位数字的基偶性,为偶则舍去,为奇则进一位。这种舍入规则被称为银行家算法,通常用来计算银行利息,因为如果采用四舍五入规则的话,当出现中位数5的时候,银行进1则会亏损这部分的钱。
| 输入数字 | 舍入结果 |
|---|---|
| 1.964 | 1.96 |
| 1.9651 | 1.96 |
| 1.965 | 1.96 |
| 1.935 | 1.94 |
| 1.966 | 1.97 |
-
保存小数位
Bigdecimal提供setScale()来设置所要保留的小数位,在进行除法计算的时候,也可以重载divide()方法:
/**
*@param newScale 要保留的小数位数
*@param roundingMode 舍入规则
*/
public BigDecimal setScale(int newScale, RoundingMode roundingMode) {
return setScale(newScale, roundingMode.oldMode);
}
a.divide(b,2,RoundingMode.UP)
-
Bigdecimal转换输出String、科学计数法、去除多余0
Bigdecimal在较为简单的输出时,可以正常输出期望数值,但是存在特殊情况:
BigDecimal a = new BigDecimal("110.00");
System.out.println(a.toString());
System.out.println(a.toPlainString());
// 其中stripTrailingZeros()为去除多余的0
System.out.println(a.stripTrailingZeros());
System.out.println(a.stripTrailingZeros().toPlainString());
System.out.println( new BigDecimal("0.000000000000").toString());
输出结果为:
110.00
110.00
1.1E+2
110
0E-12
可以看出在特殊情况下,输出结果变成了科学计数法,因此直接使用Bigdecimal提供的toPlainString()来设置不使用任何计数法可得到正常输出。