33

I've used BigDecimals before but not very often and I was working on something this morning and I kept getting the following exception:

Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion;
no exact representable decimal result.
    at java.math.BigDecimal.divide(BigDecimal.java:1594)

I was attempting to set the scale and use rounding to eliminate the problem like so:

    BigDecimal bd1 = new BigDecimal(1131).setScale(2,BigDecimal.ROUND_HALF_UP);
    BigDecimal bd2 = new BigDecimal(365).setScale(2,BigDecimal.ROUND_HALF_UP);
    BigDecimal bd3 = bd1.divide(bd2).setScale(2,BigDecimal.ROUND_HALF_UP);
    System.out.println("result: " + bd3);

However, I keep getting the same exception. Anyone able to show me where I have made a mistake?

ChadNC
  • 2,528
  • 4
  • 25
  • 39

6 Answers6

74

Non-terminating decimal need rounding

When using divide you should use a MathContext with RoundingMode in case the exact result has an infinite number of decimals.

Such is your case:

MathContext mc = new MathContext(2, RoundingMode.HALF_UP) ;
BigDecimal bd3 = bd1.divide(bd2, mc);

Alternatively call divide with a rounding mode to use the scale of the numerator (bd1 in the example below):

BigDecimal bd3 = bd1.divide(bd2, RoundingMode.HALF_UP);
assylias
  • 321,522
  • 82
  • 660
  • 783
  • Thanks that fixed the problem. I had tried using a MathContext also but it looks like I did that incorrectly. – ChadNC May 15 '12 at 15:17
  • 1
    I like the fact that you showed both ways. At first I thought, why would anyone ever use the `MathContext`. Then it dawned on me. Duh... for consistency... – buzzsawddog Jan 07 '14 at 15:48
  • I do `new BigDecimal(350000.00).divide(new BigDecimal(1))` and I still get the "Non-terminating decimal expansion; no exact representable decimal result" exception. I don't understand why the result is not simply 350000, can you elaborate please ? – David Hofmann Jan 25 '15 at 19:04
  • @DavidHofmann That line returns 350000 as expected... I suggest you ask a separate question with a complete example that shows your problem. – assylias Jan 26 '15 at 09:56
  • You say an alternative is to call divide with a scale and rounding mode, but in your example you don't actually show a scale value being passed. – Michael Mar 24 '22 at 06:09
  • 1
    @Michael the scale of bd1 is used (see the javadoc by clicking on the link). I will clarify. – assylias Mar 24 '22 at 08:09
  • 1
    @assylias Thanks for the information. The JavaDoc actually mentions this but when I read it the first time it didn't immediately jump out at me what it meant, it makes sense now: "whose value is (this / divisor), and whose preferred scale is (this.scale() - divisor.scale());" – Michael Mar 24 '22 at 15:47
4

Here's the problem

bd1.divide(bd2)

You need to use one of the overloaded divide() methods that takes a rounding mode (in various forms) - you cannot do the rounding after the division because with a nonterminating fraction the intermediate result would either already need to be rounded, or require infinite storage space.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
4

The problem is caused by an operation (division) that would result in a recurring decimal.

The solution is to specify a scale when performing a division, for example:

BigDecimal one = new BigDecimal("1");
BigDecimal three = new BigDecimal("3");
BigDecimal oneDivThree = one.divide(three, 200, RoundingMode.HALF_UP);
Neil Coffey
  • 21,615
  • 7
  • 62
  • 83
2

You are getting this error because of the division:

  1. The BigDecimal by default always tries to return the exact result of an operation.
  2. Due to this, certain division operations like 1 divided by 3, the exact quotient will have an infinitely long decimal expansion. This will cause the division operation to fail and throw the error as described above.

Provide a scale and a rounding mode (as you've done when creating the BigDecimals) within the division method to prevent this.

Taken from here. Example code for working division is also provided.

See also: Java docs on BigDecimal.

keyser
  • 18,829
  • 16
  • 59
  • 101
  • Seemed to not be your words, so I googled it, and found this. http://jaydeepm.wordpress.com/2009/06/04/bigdecimal-and-non-terminating-decimal-expansion-error/ Lol. – FThompson May 15 '12 at 15:13
  • @Vulcan they are not, but I always link to sources. Simply making the answer complete first. Most good answer will be quotes. – keyser May 15 '12 at 15:14
  • 1
    Isn't it the scale, not the RoundingMode, that fixes the problem? – Neil Coffey May 15 '12 at 15:17
  • @NeilCoffey Yes, that's correct. I'll edit. – keyser May 15 '12 at 15:18
  • @Keyser Understanable, I just am not completely sure of any plagiarism-related code of conduct on here, so I provided the link for everyone's sake. – FThompson May 15 '12 at 15:20
  • 1
    @Vulcan When providing sources I think it should be encouraged. – keyser May 15 '12 at 15:23
2

By default, BigDecimal attempts to return the exact value of the given division expression. Because of this, if the exact value cannot be determined due to an infinite decimal expansion, an ArithmeticException is thrown. A basic example of this is dividing 1 by 3, resulting in one third, a value which cannot be represented exactly in decimal notation.

FThompson
  • 28,352
  • 13
  • 60
  • 93
0

To fix, you should change the third stament as follow:

BigDecimal bd1 = new BigDecimal(1131).setScale(2,BigDecimal.ROUND_HALF_UP);
BigDecimal bd2 = new BigDecimal(365).setScale(2,BigDecimal.ROUND_HALF_UP);
BigDecimal bd3 = bd1.divide(bd2, 2, BigDecimal.ROUND_HALF_UP);
System.out.println("result: " + bd3);
The Tran
  • 400
  • 4
  • 6