2

I want to use BigDecimal in some calculations. Imagine that in some intermediate step there is the following division new BigDecimal(1).divide(new BigDecimal(4)). I understand that in this case the scale is calculated automatically and the result is 0.25. But Imagine that in some cases this fraction cannot be represented as a decimal expanstion like in new BigDecimal(1).divide(new BigDecimal(3)) then this exception is thrown java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result. cause no scale was set.

My question is: I don't want to force a fixed scale when it is not necessary, but I don't want it to throw an exception either. How can I accomplish that? Is there a way to say "Use this scale when you don't know what to do"?. If so, how can I do that, and what would be a reasonable value for the scale in an interest calculation?

Michael
  • 41,989
  • 11
  • 82
  • 128
Diego Marin Santos
  • 1,923
  • 2
  • 15
  • 29
  • 1
    The simplest solution is probably to `try` and `catch(ArithmeticException ...) { ... }`, using a fixed scale in the `catch` block; this will have a performance impact since throwing and catching exceptions isn't as fast as other control flow structures, but it may be sufficient for your use-case. *"What would be a reasonable value for the scale in an interest calculation?"* This part seems unrelated to your actual programming question, and is a matter of opinion. – kaya3 Mar 24 '21 at 18:00
  • Just to add to what @kaya3 has mentioned, check https://stackoverflow.com/questions/4591206/arithmeticexception-non-terminating-decimal-expansion-no-exact-representable for what you need to do in the catch block. – Arvind Kumar Avinash Mar 24 '21 at 18:02
  • 2
    Alternatively, have you considered using a library for exact rational arithmetic, like the Apache Commons [BigFraction](https://commons.apache.org/proper/commons-math/javadocs/api-3.3/org/apache/commons/math3/fraction/BigFraction.html) class? – kaya3 Mar 24 '21 at 18:03

3 Answers3

1

There's no ergonomic setting in the API as far as I'm aware, but why not just do:

try { 
   //do you're thing
} catch(ArithmeticException e) {
   //if it fails, then set the scale 
   x.setScale(...)
}
Amir Afghani
  • 37,814
  • 16
  • 84
  • 124
  • `BigDecimal` objects are immutable; calling `x.setScale()` has no effect unless you store the result. But storing the result wouldn't solve the OP's problem either, as `BigDecimal.divide(BigDecimal)` throws an exception if the result is non-terminating regardless of the scale. – Erwin Bolwidt Mar 25 '21 at 11:49
1

You need to first check what is the nature of the result of the division. If it is a price, then set the scale to 2 beforehand. If it is a rate (e.g. an exchange rate, an interest rate, etc.) then choose a suitable scale to make your calculations sufficiently precise, and then you can call stripTrailingZeros if you like to make the presentation nicer:

BigDecimal.valueOf(1).divide(BigDecimal.valueOf(3), 8, RoundingMode.HALF_EVEN)
    ➛ 0.33333333
BigDecimal.valueOf(1).divide(BigDecimal.valueOf(4), 8, RoundingMode.HALF_EVEN)
    ➛ 0.25000000
BigDecimal.valueOf(1).divide(BigDecimal.valueOf(4), 8, RoundingMode.HALF_EVEN)
        .stripTrailingZeros()
    ➛ 0.25

However, if you are changing the scale purely for presentational reasons, I would prefer to leave the scale at a constant 2 for prices and 8 (or higher) for rates, and then use suitable formatting to suppress trailing zeros when you eventually come to printing out the values:

new DecimalFormat("#.##########").format(new BigDecimal("12.340000000"))
    ➛ "12.34"
k314159
  • 5,051
  • 10
  • 32
0

There is no other possible way's that's why ArithmeticException introduced.

either you need to use scale.

a.divide(b, 2, RoundingMode.HALF_EVEN)

or Catch an Exception and perform the custom operation as explained by @Amir.

Raushan Kumar
  • 1,195
  • 12
  • 21