除法运算要取余

也就是说,两个 BigDecimal 在做除法运算时一定要做小数保留。比如下面的代码:

  1. BigDecimal divisor = new BigDecimal("1");
  2. BigDecimal dividend = new BigDecimal("3");
  3. BigDecimal result = divisor.divide(dividend);

如果使用 IntelliJ IDEA 开发工具的话,你会看到 result 表达式会有一个波浪线提示,内容如下:

image.png

为什么会提示一个需要舍入模式参数呢?

如果不管这个取舍模式而直接运行的话会抛出如下异常:

  1. Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
  2. at java.math.BigDecimal.divide(BigDecimal.java:1693)
  3. at com.itumate.docker.rocket.Main.main(Main.java:11)

这是因为 1 / 3 是除不尽的,结果是一个无线循环小数。没法使用 BigDecimal 存储,所以需要做小数取舍。

在实际使用中应该以下面的两个方法为主:

  1. java.math.BigDecimal#divide(BigDecimal divisor, int scale, int roundingMode);
  2. java.math.BigDecimal#divide(BigDecimal divisor, int scale, RoundingMode roundingMode);

其中 **scale** 指的是小数的保留位数,比如保留小数后两位就直接给个 2 就好了。

**roundingMode****roundingMode**表示取舍模式,只不过一个使用的是数值一个使用的是枚举类表示,在实际使用中推荐使用枚举类 **java.math.RoundingMode** 作为参数,因为使用 roundingMode 会有语义不清的问题。

另外,在枚举类 RoundingMode 中有一个 valueOf() 方法,该方法就是用于 roundingModeroundingMode 的转换,源码如下:

  1. public enum RoundingMode {
  2. // 枚举值略
  3. // 数值 rm 转枚举类, 即第一个方法的第三那个参数与 RoundingMode 的对应关系
  4. public static RoundingMode valueOf(int rm) {
  5. switch(rm) {
  6. case BigDecimal.ROUND_UP:
  7. return UP;
  8. case BigDecimal.ROUND_DOWN:
  9. return DOWN;
  10. case BigDecimal.ROUND_CEILING:
  11. return CEILING;
  12. case BigDecimal.ROUND_FLOOR:
  13. return FLOOR;
  14. case BigDecimal.ROUND_HALF_UP:
  15. return HALF_UP;
  16. case BigDecimal.ROUND_HALF_DOWN:
  17. return HALF_DOWN;
  18. case BigDecimal.ROUND_HALF_EVEN:
  19. return HALF_EVEN;
  20. case BigDecimal.ROUND_UNNECESSARY:
  21. return UNNECESSARY;
  22. default:
  23. throw new IllegalArgumentException("argument out of range");
  24. }
  25. }
  26. }

java.math.RoundingMode 枚举类中定义了多个取舍模式,稍后会做具体说明,在实际中使用最多的就是 **RoundingMode#HALF_UP** 模式(即四舍五入)

知道了这些后我们再将上面的代码修改如下即可:

  1. BigDecimal divisor = new BigDecimal("1");
  2. BigDecimal dividend = new BigDecimal("3");
  3. // 四舍五入保留两位小数
  4. 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** 取舍模式就是我们通常使用的四舍五入模式,不过在实际使用中该跟怎么去选择还是看实际业务需求吧。