星星之火-使用BigDecimal时的非整除报错问题

本文最后更新于:August 20, 2022 pm

积土成山,风雨兴焉;积水成渊,蛟龙生焉;积善成德,而神明自得,圣心备焉。故不积跬步,无以至千里,不积小流无以成江海。齐骥一跃,不能十步,驽马十驾,功不在舍。面对悬崖峭壁,一百年也看不出一条裂缝来,但用斧凿,能进一寸进一寸,能进一尺进一尺,不断积累,飞跃必来,突破随之。

目录

今天在使用大数BigDecimal时,出现了一个问题。先附上报错以及原因。

  • 报错

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

  • 原因

在用BigDecimal做除法的时候,遇到了不整除的情况,而且造成的结果是无限循环小数,就会抛出以上异常。

示例

  • 整除
1
2
3
BigDecimal b1 = new BigDecimal(10);
BigDecimal b2 = new BigDecimal(2);
System.out.println(b1.divide(b2)); // 5
  • 非整除(结果为有限小数)
1
2
3
BigDecimal b1 = new BigDecimal(10);
BigDecimal b2 = new BigDecimal(4);
System.out.println(b1.divide(b2)); // 2.5
  • 非整除(结果为无限小数)
1
2
3
4
5
6
BigDecimal b1 = new BigDecimal(10);
BigDecimal b2 = new BigDecimal(3);
System.out.println(b1.divide(b2));

// 报错
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

解决办法

只需要在 divide() 方法指定精度和舍入模式就可以解决该问题。

  • 八种舍入模式
常量名 描述
ROUND_UP 远离零方向舍入。向远离0的方向舍入,也就是说,向绝对值最大的方向舍入,只要舍弃位非0即进位。
ROUND_DOWN 趋向0方向舍入。向0方向靠拢,也就是说,向绝对值最小的方向输入,注意:所有的位都舍弃,不存在进位情况。
ROUND_CEILING 向正无穷方向舍入。向正最大方向靠拢,如果是正数,舍入行为类似于ROUND_UP;如果为负数,则舍入行为类似于ROUND_DOWN.注意:Math.round方法使用的即为此模式。
ROUND_FLOOR 向负无穷方向舍入。向负无穷方向靠拢,如果是正数,则舍入行为类似ROUND_DOWN,如果是负数,舍入行为类似以ROUND_UP。
ROUND_HALF_UP 最近数字舍入(5舍),这就是我们经典的四舍五入。
ROUND_HALF_DOWN 最近数字舍入(5舍)。在四舍五入中,5是进位的,在HALF_DOWN中却是舍弃不进位。
ROUND_HALF_EVEN 银行家算法。四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。
ROUND_UNNECESSARY 断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。

舍入模式示例

示例一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BigDecimal b1 = new BigDecimal(10);
BigDecimal b2 = new BigDecimal(3);
System.out.println(10.0/3.0);
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_UP));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_DOWN));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_CEILING));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_FLOOR));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_HALF_UP));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_HALF_DOWN));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_HALF_EVEN));

// 输出
3.3333333333333335
ROUND_UP:3.4
ROUND_DOWN:3.3
ROUND_CEILING:3.4
ROUND_FLOOR:3.3
ROUND_HALF_UP:3.3
ROUND_HALF_DOWN:3.3
ROUND_HALF_EVEN:3.3

示例二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BigDecimal b1 = new BigDecimal(12345);
BigDecimal b2 = new BigDecimal(1000);
System.out.println(12345.0/1000.0);
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_UP));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_DOWN));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_CEILING));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_FLOOR));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_HALF_UP));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_HALF_DOWN));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_HALF_EVEN));

// 输出
12.345
ROUND_UP:12.4
ROUND_DOWN:12.3
ROUND_CEILING:12.4
ROUND_FLOOR:12.3
ROUND_HALF_UP:12.3
ROUND_HALF_DOWN:12.3
ROUND_HALF_EVEN:12.3

示例三

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BigDecimal b1 = new BigDecimal(12456);
BigDecimal b2 = new BigDecimal(1000);
System.out.println(12456.0/1000.0);
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_UP));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_DOWN));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_CEILING));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_FLOOR));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_HALF_UP));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_HALF_DOWN));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_HALF_EVEN));

// 输出
12.456
ROUND_UP:12.5
ROUND_DOWN:12.4
ROUND_CEILING:12.5
ROUND_FLOOR:12.4
ROUND_HALF_UP:12.5
ROUND_HALF_DOWN:12.5
ROUND_HALF_EVEN:12.5

示例四

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BigDecimal b1 = new BigDecimal(12567);
BigDecimal b2 = new BigDecimal(1000);
System.out.println(12567.0/1000.0);
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_UP));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_DOWN));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_CEILING));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_FLOOR));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_HALF_UP));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_HALF_DOWN));
System.out.println(b1.divide(b2,1,BigDecimal.ROUND_HALF_EVEN));

// 输出
12.567
ROUND_UP:12.6
ROUND_DOWN:12.5
ROUND_CEILING:12.6
ROUND_FLOOR:12.5
ROUND_HALF_UP:12.6
ROUND_HALF_DOWN:12.6
ROUND_HALF_EVEN:12.6