Total Pageviews

2015/08/03

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

Problem
Here is my code snippet.

If amtIn is 100 and amtOut is 20, then it works fine.
1
2
3
4
5
6
  private BigDecimal toRate(final BigDecimal amtIn, final BigDecimal amtOut) {
      if (null == amtIn || null == amtOut) {
          return null;
      }
      return amtIn.divide(amtOut).multiply(new BigDecimal(100));
  }


But if amtIn is 100 and amtOut is 120, then it will throw exception unexpectedly
1
2
3
4
16:40:15,812 INFO  [stdout] java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
16:40:15,812 INFO  [stdout]   at java.math.BigDecimal.divide(BigDecimal.java:1616) ~[na:1.7.0_25]
16:40:15,812 INFO  [stdout]   at gov.nta.dbm.service.Dbm034eService.toRate(Dbm034eService.java:264) ~[dbm-service-1.0.0-SNAPSHOT.jar:1.0.0-SNAPSHOT]
16:40:15,812 INFO  [stdout]   at gov.nta.dbm.service.Dbm034eService.modify(Dbm034eService.java:359) ~[dbm-service-1.0.0-SNAPSHOT.jar:1.0.0-SNAPSHOT

Solution
According to JavaDoc...
When a MathContext object is supplied with a precision setting of 0 (for example, MathContext.UNLIMITED), arithmetic operations are exact, as are the arithmetic methods which take no MathContext object. (This is the only behavior that was supported in releases prior to 5.)
As a corollary of computing the exact result, the rounding mode setting of a MathContext object with a precision setting of 0 is not used and thus irrelevant. In the case of divide, the exact quotient could have an infinitely long decimal expansion; for example, 1 divided by 3.
If the quotient has a nonterminating decimal expansion and the operation is specified to return an exact result, an ArithmeticException is thrown. Otherwise, the exact result of the division is returned, as done for other operations.

Owing to 100/120 has a nonterminating decimal expansion, so it will throw java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result

To resolve this problem, you can set precision to 2, and set rounding mode to RoundingMode.HALF_UP (precision and rounding mode depends on your requirement)
1
2
3
4
5
6
  private BigDecimal toRate(final BigDecimal amtIn, final BigDecimal amtOut) {
      if (null == amtIn || null == amtOut) {
          return null;
      }
      return amtIn.divide(amtOut, 2, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100));
  }

Reference
[1] http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
[2] http://elie2201.blogspot.tw/2012/03/bigdecimal-non-terminating-decimal.html

No comments: