3

I know that I can use BigDecimal.divide() method to set a fixed precision:

BigDecimal a1 = new BigDecimal(1);
BigDecimal a2 = new BigDecimal(3);
BigDecimal division = a1.divide(a2,5,1); //equals 0.33333

But if the division result is exact:

BigDecimal a1 = new BigDecimal(1);
BigDecimal a2 = new BigDecimal(4);
BigDecimal division = a1.divide(a2,5,1); //equals 0.25000

How can I set the division result so if the division ends with an exact result, it will just give the output of 0.25 instead of 0.25000?

I've also tried to not specifying a precision by doing:

BigDecimal division = a1.divide(a2);

it succeeds in giving the result 0.25 when doing 1/4, but when it does division like 1/3 or 2/3 it results in a runtime ArithmeticException.

a1 and a2 are instantiated from user input.

Is there any way to solve this?

dimo414
  • 47,227
  • 18
  • 148
  • 244
user3411184
  • 71
  • 1
  • 10
  • You should prefer the [`RoundingMode`](https://docs.oracle.com/javase/8/docs/api/java/math/RoundingMode.html) class over passing in integer rounding mode values. Even if you must use the integer methods for whatever reason, use the constants in `BigDecimal`, e.g. [`BigDecimal.ROUND_DOWN`](https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html#ROUND_DOWN). – dimo414 May 06 '15 at 05:31
  • oh I see, I was not really sure which to choose so I just use int instead. But thanks now I understand. – user3411184 May 06 '15 at 06:34

1 Answers1

6

You get an ArithmeticException as described in BigDecimal.divide(BigDecimal) - "if the exact quotient does not have a terminating decimal expansion" because to properly represent the result of 1/3 would take infinite memory. By using BigDecimal.divide(BigDecimal,int,RoundingMode) you explicitly set what precision you'll tolerate, meaning any division can be approximated in-memory.

For consistency this version of division always sets the scale to what you specify, even if the value can be accurately represented with less precision. If it didn't, it would be difficult for you to reason about how precise any future computations using that result will be.

To reduce your precision use .stripTrailingZeros(); this effectively reduces the scale to the minimum possible. Do this as late in the process as possible (i.e. right before you print) to maintain that consistency I mentioned above.

You may also want .toPlainString() if you're trying to display these values nicely, since .stripTrailingZeros() on its own will display 100 as 1E+2.

dimo414
  • 47,227
  • 18
  • 148
  • 244
  • Similar: http://stackoverflow.com/questions/25541684/how-to-remove-trailing-zero-in-a-string-value-and-remove-decimal-point/25542023#25542023 (Yes, this is shameless self-promotion) – jdphenix May 06 '15 at 05:28
  • It may be worth mentioning `BigDecimal(string)` versus `BigDecimal(double)`, and how the latter can negatively impact formatting output – jdphenix May 06 '15 at 05:32
  • @jdphenix OP is using [`BigDecimal(int)`](https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html#BigDecimal-int-) which is lossless - I don't see how the limitations of the [`BigDecimal(double)`](https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html#BigDecimal-double-) constructor are relevant to this question, but there's a link to the constructor's documentation if it helps anyone. – dimo414 May 06 '15 at 05:36
  • I'm going to convert BigInt value to BigDecimal though so there is no loss I guess. Thanks a lot anyway. It works really well, did not know such function exist.. – user3411184 May 06 '15 at 06:31