问题描述
在工作中,使用到BigDecimal类进行除法运算,结果系统在运行的过程中,发生了如下错误信息:
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1693)
翻译成中文的大体意思是没有终止的decimal 扩张,没有确切的可表达的deciaml结果。
翻译成人话就是除不尽。示例代码如下
BigDecimal divide = new BigDecimal(list.size).divide(new BigDecimal(slices));
说明list.size 除以slices 无法除尽。是不是这个原因呢?我们来做一个实验,看下面的代码:
BigDecimal divide = new BigDecimal(20).divide(new BigDecimal(3));
运行后,果然抛上面的错误。
将参数修改成
BigDecimal divide = new BigDecimal(20).divide(new BigDecimal(4));
运行后得到结果 5。说明就是这个问题,那么如何解决呢?
解决方式
既然是因为除不尽导致的,那么我们在除的时候指定进位方式:根据实际业务选择对应的进位方式
BigDecimal divide = new BigDecimal(22).divide(new BigDecimal(4), RoundingMode.UP);
进位方式
1、RoundingMode.ROUND_UP:只要小数位不等于0,无条件向上取整(与向大取整不同)
官方给的例子如下:
<table>
<tr align=right><td>5.5</td> <td>6</td>
<tr align=right><td>2.5</td> <td>3</td>
<tr align=right><td>1.6</td> <td>2</td>
<tr align=right><td>1.1</td> <td>2</td>
<tr align=right><td>1.0</td> <td>1</td>
<tr align=right><td>-1.0</td> <td>-1</td>
<tr align=right><td>-1.1</td> <td>-2</td>
<tr align=right><td>-1.6</td> <td>-2</td>
<tr align=right><td>-2.5</td> <td>-3</td>
<tr align=right><td>-5.5</td> <td>-6</td>
</table>
2、RoundingMode.ROUND_DOWN:与向上取整相反,向下取整
<table>
<tr align=right><td>5.5</td> <td>5</td>
<tr align=right><td>2.5</td> <td>2</td>
<tr align=right><td>1.6</td> <td>1</td>
<tr align=right><td>1.1</td> <td>1</td>
<tr align=right><td>1.0</td> <td>1</td>
<tr align=right><td>-1.0</td> <td>-1</td>
<tr align=right><td>-1.1</td> <td>-1</td>
<tr align=right><td>-1.6</td> <td>-1</td>
<tr align=right><td>-2.5</td> <td>-2</td>
<tr align=right><td>-5.5</td> <td>-5</td>
</table>
3、RoundingMode.ROUND_CEILING:向大取整,意思是向大的方向取整
<table>
<tr align=right><td>5.5</td> <td>6</td>
<tr align=right><td>2.5</td> <td>3</td>
<tr align=right><td>1.6</td> <td>2</td>
<tr align=right><td>1.1</td> <td>2</td>
<tr align=right><td>1.0</td> <td>1</td>
<tr align=right><td>-1.0</td> <td>-1</td>
<tr align=right><td>-1.1</td> <td>-1</td>
<tr align=right><td>-1.6</td> <td>-1</td>
<tr align=right><td>-2.5</td> <td>-2</td>
<tr align=right><td>-5.5</td> <td>-5</td>
</table>
4、RoundingMode.ROUND_FLOOR:向小的方向取整
<table>
<tr align=right><td>5.5</td> <td>5</td>
<tr align=right><td>2.5</td> <td>2</td>
<tr align=right><td>1.6</td> <td>1</td>
<tr align=right><td>1.1</td> <td>1</td>
<tr align=right><td>1.0</td> <td>1</td>
<tr align=right><td>-1.0</td> <td>-1</td>
<tr align=right><td>-1.1</td> <td>-2</td>
<tr align=right><td>-1.6</td> <td>-2</td>
<tr align=right><td>-2.5</td> <td>-3</td>
<tr align=right><td>-5.5</td> <td>-6</td>
</table>
5、RoundingMode.ROUND_HALF_UP:半向相邻的方向取整,与哪个整数近,取哪个,0.5向上取整,如果是正数,0.5向大取整,如果是负数,0.5向小取整
<table>
<tr align=right><td>5.5</td> <td>6</td>
<tr align=right><td>2.5</td> <td>3</td>
<tr align=right><td>1.6</td> <td>2</td>
<tr align=right><td>1.1</td> <td>1</td>
<tr align=right><td>1.0</td> <td>1</td>
<tr align=right><td>-1.0</td> <td>-1</td>
<tr align=right><td>-1.1</td> <td>-1</td>
<tr align=right><td>-1.6</td> <td>-2</td>
<tr align=right><td>-2.5</td> <td>-3</td>
<tr align=right><td>-5.5</td> <td>-6</td>
</table>
6、RoundingMode.ROUND_HALF_DOWN:半向相邻的方向取整,与哪个整数近,取哪个,0.5向下取整,如果是正数,0.5向小取整,如果是负数,0.5向大取整
<table>
<tr align=right><td>5.5</td> <td>5</td>
<tr align=right><td>2.5</td> <td>2</td>
<tr align=right><td>1.6</td> <td>2</td>
<tr align=right><td>1.1</td> <td>1</td>
<tr align=right><td>1.0</td> <td>1</td>
<tr align=right><td>-1.0</td> <td>-1</td>
<tr align=right><td>-1.1</td> <td>-1</td>
<tr align=right><td>-1.6</td> <td>-2</td>
<tr align=right><td>-2.5</td> <td>-2</td>
<tr align=right><td>-5.5</td> <td>-5</td>
</table>
7、RoundingMode.ROUND_HALF_EVEN:半偶数取整,主要针对半数0.5来说,向偶数取整
<table>
<tr align=right><td>5.5</td> <td>6</td>
<tr align=right><td>2.5</td> <td>2</td>
<tr align=right><td>1.6</td> <td>2</td>
<tr align=right><td>1.1</td> <td>1</td>
<tr align=right><td>1.0</td> <td>1</td>
<tr align=right><td>-1.0</td> <td>-1</td>
<tr align=right><td>-1.1</td> <td>-1</td>
<tr align=right><td>-1.6</td> <td>-2</td>
<tr align=right><td>-2.5</td> <td>-2</td>
<tr align=right><td>-5.5</td> <td>-6</td>
</table>
8、RoundingMode.ROUND_UNNECESSARY:如果是小数则抛出异常
<table>
<tr align=right><td>5.5</td> <td>throw {@code ArithmeticException}</td>
<tr align=right><td>2.5</td> <td>throw {@code ArithmeticException}</td>
<tr align=right><td>1.6</td> <td>throw {@code ArithmeticException}</td>
<tr align=right><td>1.1</td> <td>throw {@code ArithmeticException}</td>
<tr align=right><td>1.0</td> <td>1</td>
<tr align=right><td>-1.0</td> <td>-1</td>
<tr align=right><td>-1.1</td> <td>throw {@code ArithmeticException}</td>
<tr align=right><td>-1.6</td> <td>throw {@code ArithmeticException}</td>
<tr align=right><td>-2.5</td> <td>throw {@code ArithmeticException}</td>
<tr align=right><td>-5.5</td> <td>throw {@code ArithmeticException}</td>
</table>
在代码中找到commonNeedIncrement方法,用来 判断是否需要进位
private static boolean commonNeedIncrement(int roundingMode, int qsign,
int cmpFracHalf, boolean oddQuot) {
switch(roundingMode) {
case ROUND_UNNECESSARY:
throw new ArithmeticException("Rounding necessary");
case ROUND_UP: // Away from zero
return true;
case ROUND_DOWN: // Towards zero
return false;
case ROUND_CEILING: // Towards +infinity
return qsign > 0;
case ROUND_FLOOR: // Towards -infinity
return qsign < 0;
default: // Some kind of half-way rounding
assert roundingMode >= ROUND_HALF_UP &&
roundingMode <= ROUND_HALF_EVEN: "Unexpected rounding mode" + RoundingMode.valueOf(roundingMode);
if (cmpFracHalf < 0 ) // We're closer to higher digit
return false;
else if (cmpFracHalf > 0 ) // We're closer to lower digit
return true;
else { // half-way
assert cmpFracHalf == 0;
switch(roundingMode) {
case ROUND_HALF_DOWN:
return false;
case ROUND_HALF_UP:
return true;
case ROUND_HALF_EVEN:
return oddQuot;
default:
throw new AssertionError("Unexpected rounding mode" + roundingMode);
}
}
}
}