68

What is an efficient way of determining whether a BigDecimal is an integer value in the mathematical sense?

At present I have the following code:

private boolean isIntegerValue(BigDecimal bd) {
    boolean ret;
    
    try {
        bd.toBigIntegerExact();
        ret = true;
    } catch (ArithmeticException ex) {
        ret = false;
    }

    return ret;
}

... but would like to avoid the object creation overhead if possible.

Previously I was using bd.longValueExact() which would avoid creating an object if the BigDecimal was using its compact representation internally, but obviously would fail if the value was too big to fit into a long.

M. Justin
  • 14,487
  • 7
  • 91
  • 130
Adamski
  • 54,009
  • 15
  • 113
  • 152
  • 3
    Weird - I can see 5 responses on my profile but when I navigate to the question I only see these two. Is this by design? (i.e. Are response suppressed after I accept an answer?) – Adamski Jul 03 '09 at 12:03

7 Answers7

102

EDIT: As of Java 8, stripTrailingZeroes() now accounts for zero

BigDecimal stripTrailingZeros doesn't work for zero

So

private boolean isIntegerValue(BigDecimal bd) {
  return bd.stripTrailingZeros().scale() <= 0;
}

Is perfectly fine now.


If you use the scale() and stripTrailingZeros() solution mentioned in some of the answers you should pay attention to zero. Zero always is an integer no matter what scale it has, and stripTrailingZeros() does not alter the scale of a zero BigDecimal.

So you could do something like this:

private boolean isIntegerValue(BigDecimal bd) {
  return bd.signum() == 0 || bd.scale() <= 0 || bd.stripTrailingZeros().scale() <= 0;
}
Jerry Jiang
  • 3
  • 1
  • 2
tobi
  • 1,205
  • 2
  • 8
  • 8
19

Depending on the source/usage of your BigDecimal values it might be faster to check if the scale <= 0 first. If it is, then it's definitely an integer value in the mathematical sense. If it is >0, then it could still be an integer value and the more expensive test would be needed.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • Thanks - Don't know why I didn't think of that, and it's a good optimisation for as this method as I expect the check to pass 99% of the time. – Adamski Jul 03 '09 at 11:44
  • It doesn't work with (new BigDecimal("0.0")).scale() – Sank Nov 25 '22 at 07:58
  • @Sank: did you read the full answer? What you provided is one of those cases where scale alone doesn't give the answer. If you need the full code, feel free to refer to the answer by tobi. – Joachim Sauer Nov 25 '22 at 09:43
  • Yes, tobby's answer is correct – Sank Nov 27 '22 at 07:33
8

Divide the number by 1 and check for a remainder. Any whole number should always have a remainder of 0 when divided by 1.

public boolean isWholeNumber(BigDecimal number) {
    return number.remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) == 0;
}
ooxi
  • 3,159
  • 2
  • 28
  • 41
StPatrick
  • 190
  • 3
  • 8
  • 2
    It might be worth adding in `number.scale() <= 0 || ...` before calculating the remainder and checking against zero, to avoid unnecessary overhead when the scale indicates it's definitely an integer. – daiscog Jan 08 '16 at 15:56
6

You can use this (just summarizing from other answers):

private boolean isIntegerValue(BigDecimal bd) {
  return bd.stripTrailingZeros().scale() <= 0;
}
emisch
  • 85
  • 1
  • 1
5

This can be done the same way that someone would check if a double is an integer, performing % 1 == 0. This is how it would look for a BigDecimal value.

public static boolean isIntegerValue(BigDecimal bd){
  return bd.remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) == 0;
}
Sadeq Dousti
  • 3,346
  • 6
  • 35
  • 53
4

One possiblity should be to check if scale() is zero or negative. In that case the BigDecimal should have no digits after the decimal point and the number should be a mathematical integer if I understand your question correctly.

Update: If positive, it could still be an integer, but you cannot spare extra object creations for further in-depth checks in that case. An example for such a case is given at the stripTrailingZeros() method javadoc (thanks Joachim for the hint in his answer).

Kosi2801
  • 22,222
  • 13
  • 38
  • 45
1

This is the cleanest I've come up with.

public static boolean isWhole(BigDecimal bigDecimal) {
    return bigDecimal.setScale(0, RoundingMode.HALF_UP).compareTo(bigDecimal) == 0;
}
Ian
  • 11
  • 3