除法运算要取余
也就是说,两个 BigDecimal 在做除法运算时一定要做小数保留。比如下面的代码:
BigDecimal divisor = new BigDecimal("1");
BigDecimal dividend = new BigDecimal("3");
BigDecimal result = divisor.divide(dividend);
如果使用 IntelliJ IDEA 开发工具的话,你会看到 result 表达式会有一个波浪线提示,内容如下:
为什么会提示一个需要舍入模式参数呢?
如果不管这个取舍模式而直接运行的话会抛出如下异常:
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1693)
at com.itumate.docker.rocket.Main.main(Main.java:11)
这是因为 1 / 3 是除不尽的,结果是一个无线循环小数。没法使用 BigDecimal 存储,所以需要做小数取舍。
在实际使用中应该以下面的两个方法为主:
java.math.BigDecimal#divide(BigDecimal divisor, int scale, int roundingMode);
java.math.BigDecimal#divide(BigDecimal divisor, int scale, RoundingMode roundingMode);
其中 **scale**
指的是小数的保留位数,比如保留小数后两位就直接给个 2 就好了。
**roundingMode**
和 **roundingMode**
都表示取舍模式,只不过一个使用的是数值一个使用的是枚举类表示,在实际使用中推荐使用枚举类 **java.math.RoundingMode**
作为参数,因为使用 roundingMode
会有语义不清的问题。
另外,在枚举类 RoundingMode
中有一个 valueOf()
方法,该方法就是用于 roundingMode
到 roundingMode
的转换,源码如下:
public enum RoundingMode {
// 枚举值略
// 数值 rm 转枚举类, 即第一个方法的第三那个参数与 RoundingMode 的对应关系
public static RoundingMode valueOf(int rm) {
switch(rm) {
case BigDecimal.ROUND_UP:
return UP;
case BigDecimal.ROUND_DOWN:
return DOWN;
case BigDecimal.ROUND_CEILING:
return CEILING;
case BigDecimal.ROUND_FLOOR:
return FLOOR;
case BigDecimal.ROUND_HALF_UP:
return HALF_UP;
case BigDecimal.ROUND_HALF_DOWN:
return HALF_DOWN;
case BigDecimal.ROUND_HALF_EVEN:
return HALF_EVEN;
case BigDecimal.ROUND_UNNECESSARY:
return UNNECESSARY;
default:
throw new IllegalArgumentException("argument out of range");
}
}
}
在 java.math.RoundingMode
枚举类中定义了多个取舍模式,稍后会做具体说明,在实际中使用最多的就是 **RoundingMode#HALF_UP**
模式(即四舍五入)。
知道了这些后我们再将上面的代码修改如下即可:
BigDecimal divisor = new BigDecimal("1");
BigDecimal dividend = new BigDecimal("3");
// 四舍五入保留两位小数
BigDecimal result = divisor.divide(dividend, 2, RoundingMode.HALF_UP);
这样,再使用 BigDecimal 做除法运算就不用担心无限小数的问题了。
下面来看下具体的取舍模式:
RoundingMode 取舍模式
下面是 RoundingMode
相关取舍模式的示例数据:
取舍模式输出结果 | ||||||||
---|---|---|---|---|---|---|---|---|
目标数值 | UP | DOWN | CEILING | FLOOR | HALF_UP | HALF_DOWN | HALF_EVEN | UNNECESSARY |
**5.5** |
6 | 5 | 6 | 5 | 6 | 5 | 6 | throw ArithmeticException |
**2.5** |
3 | 2 | 3 | 2 | 3 | 2 | 2 | throw ArithmeticException |
**1.6** |
2 | 1 | 2 | 1 | 2 | 2 | 2 | throw ArithmeticException |
**1.5** |
2 | 1 | 2 | 1 | 2 | 1 | 2 | throw ArithmeticException |
**1.4** |
2 | 1 | 2 | 1 | 1 | 1 | 1 | throw ArithmeticException |
**1.1** |
2 | 1 | 2 | 1 | 1 | 1 | 1 | throw ArithmeticException |
**1.0** |
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
**-1.0** |
-1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 |
**-1.1** |
-2 | -1 | -1 | -2 | -1 | -1 | -1 | throw ArithmeticException |
**-1.6** |
-2 | -1 | -1 | -2 | -2 | -2 | -2 | throw ArithmeticException |
**-2.5** |
-3 | -2 | -2 | -3 | -3 | -2 | -2 | throw ArithmeticException |
**-5.5** |
-6 | -5 | -5 | -6 | -6 | -5 | -6 | throw ArithmeticException |
从表格的数据可以看出,**RoundingMode#HALF_UP**
取舍模式就是我们通常使用的四舍五入模式,不过在实际使用中该跟怎么去选择还是看实际业务需求吧。