12

The requirement is to check if the number of digits is less than 7 digits in that case insert in DB else don't. I have tried the following solutions:

First solution:

public static void checkNoOfDigitVal(BigDecimal bigDecVal) {
    BigInteger digits = bigDecVal.toBigInteger();
    BigInteger ten = BigInteger.valueOf(10);
    int count = 0;
    do {
        digits = digits.divide(ten);
        count++;
    } while (!digits.equals(BigInteger.ZERO));
    System.out.println("Number of digits : " + count);
}

First solution works fine sometimes but sometimes the condition in while loop is not satisfied and it keeps on increasing the count number leading to endless count.

Second solution:

public static void checkNoOfDigitsVal(BigDecimal bigDecVal) {
    String string = bigDecVal.toString();
    String[] splitedString = string.split("\\.");
    String[] newVal = splitedString[0].split("");
    int a = newVal.length - 1;
    if (a <= 6) {
        System.out.println("correct size insert into DB: " + a);
    } else {
        System.out.println("Incorrect size insert cancel: " + a);
    }
}

For example, if the value is 999999.9999, the second solution will return newVal.length = 6.

Please suggest a better solution to check the number of digits for big decimal where looping overhead can be minimized.

buræquete
  • 14,226
  • 4
  • 44
  • 89
Harleen
  • 773
  • 3
  • 15
  • 25
  • What happens if you do this? `while (!digits.compareTo(BigInteger.ZERO) != 0);`. – Buhake Sindi Mar 04 '16 at 09:41
  • *"the second solution will return newVal.length = 7 instead it should be 6 which is incorrect"* Huh? Is 6 correct or not? – T.J. Crowder Mar 04 '16 at 09:41
  • Possible duplicate of http://stackoverflow.com/questions/2296110/determine-number-of-decimal-place-using-bigdecimal – daniel.keresztes Mar 04 '16 at 09:43
  • 1
    `String[] newVal = splitedString[0].split(""); int a = newVal.length;`? why not just `splitedString[0].length()`? – Adrian Shum Mar 04 '16 at 09:43
  • 1
    Though @Alnitak has given a great answer, I just wonder why would you want to do the decision based on "no of digits before decimal point", instead of a simple `final BigDecimal LIMIT = new BigDecimal("1_000_000"); .... if (yourValue.compareTo(LIMIT) < 0) { doYourThing();}` ? – Adrian Shum Mar 04 '16 at 10:07
  • 1
    @AdrianShum that would indeed appear to be a good solution to the "more than 6 digits requirement" :p – Alnitak Mar 04 '16 at 10:11

4 Answers4

29

You can get it trivially using:

static int integerDigits(BigDecimal n) {
    n = n.stripTrailingZeros();
    return n.precision() - n.scale();
}

The precision is the total number of digits, and the scale is how many of those are to the right of the decimal point, so the difference is how many are to the left of the decimal point.

EDIT it's necessary to remove any trailing zeros to get a correct result for e.g. 0.000

EDIT 2 alternatively (and acks to @AdrianShum), since the problem with trailing zeroes only manifests itself with 0.00... you could use:

static int integerDigits(BigDecimal n) {
    return n.signum() == 0 ? 1 : n.precision() - n.scale();
}

Live demo at http://ideone.com/uI6iMG

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • I'd rather simply do a `return (n.signum() == 0) ? 1 : ( n.precision() - n.scale());` to avoid extra work and temp object for stripping zeros – Adrian Shum Mar 04 '16 at 10:12
  • @AdrianShum yeah, that works too :) It does only appear to be 0.00... that causes the problem with `.scale()` and trailing zeroes. – Alnitak Mar 04 '16 at 10:14
  • Thanks @Alnitak for providing the great answer. Your piece of code has reduced the lines of code and looks very efficient. Bravo!!! – Harleen Mar 05 '16 at 09:43
  • The precision is not the total number of digits in case of leading zeros. `integerDigits(BigDecimal.valueOf(0.00000123456))` will return `-5`! Check [my answer](https://stackoverflow.com/a/56355974/883603) for a more robust solution. – Dormouse May 29 '19 at 08:19
  • @Dormouse - The above *seems* to work just fine with leading zeros: https://ideone.com/epmPsN Can you provide an example of where it's failing? – T.J. Crowder Apr 09 '20 at 09:22
  • NOTE: precision and scale can be equal, please consider a case: new BigDecimal("0.32") scale = 2 precision = 2 – idmitriev Jun 02 '20 at 11:35
4

There's a much better solution in Alnitak's answer, posted just after mine (but which I've only seen now). I guess I'll leave this since a couple of people have found it useful, but if I needed to do this, I'd use their approach, not the approach below.


Your second solution is close, but it doesn't have to be quite that complicated:

public static void checkNoOfDigitsVal(BigDecimal bigDecVal) {
    String str = bigDecVal.toString();
    int wholeNumberLength = str.split("\\.")[0].length();
    if (wholeNumberLength <= 6) {
        System.out.println("correct size insert into DB: " + wholeNumberLength);
    } else {
        System.out.println("Incorrect size insert cancel: " + wholeNumberLength);
    }
}

Live Example

I'm assuming that your 999999.999 example should result in wholeNumberLnegth of 6 and therefore be inserted in the DB (the question is unclear about that).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

Because the current answers are not robust enough IMO, Here's my solution. This method will scale a BigDecimal to the given length, but only scales the fractional part. It will throw an Exception if the integer part will be scaled. For my use case this is what I want. Tweak it to your liking.

public static BigDecimal scaleBigDecimalToLength(BigDecimal bigDecimal, int length) throws NumbersUtilException {
  int digitCount = bigDecimal.toPlainString().replaceAll("[.,-]", "").length();
  if (digitCount > length) {
      int scale = bigDecimal.scale();
      int newScale = length - (digitCount - scale);
      if (scale > 0 && newScale >= 0) {
          bigDecimal = bigDecimal
                  .setScale(length - (digitCount - scale), RoundingMode.HALF_UP);
      } else {
          throw new NumbersUtilException(
                  String.format("Cannot scale %s to a length of %s", bigDecimal, length));
    }
  }
  return bigDecimal;
}

scaleBigDecimalToLength(BigDecimal.valueOf(0.0000012345600000), 8) Output: 0.0000012

Dormouse
  • 1,617
  • 1
  • 23
  • 33
1
  1. If you want to ignore the Dot (".") and count. then try this :
        int count = 0;
        BigDecimal bigDecimal = new BigDecimal("123.1000");

        String[] split = bigDecimal.toString()
                .split("\\.");

        for (String element : split) {
            count = count + element.length();
        }

        System.out.println("Total digits are " + count);

JavaTech
  • 61
  • 1
  • 3