Reputation: 803
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.
Upvotes: 12
Views: 30972
Reputation: 79
Let's assume, you have a method, like:
int countDigitsBeforeDecimalPoint(BigDecimal n)
To get the number of digits before the decimal place, use this body:
return n.toBigInteger().abs().toString().length();
Or, if you want to use precision and scale - with subtracting them you always get a correct result, except the value is between -1 and 1 (exclusive), so this also works to count only numbers (no sign):
return n.compareTo(BigDecimal.ONE) == -1 && n.compareTo(new BigDecimal("-1")) == 1 ? 1 : n.precision() - n.scale();
If you also need to include the minus sign, then you should do it differently:
return StringUtils.substringBefore(n.toPlainString(), ".").length()
If you don't want to use Apache commons StringUtils, it get's a bit more weird:
String s = n.toPlainString();
int i = (i = s.indexOf('.')) > -1 ? i : s.length();
return s.substring(0, i).length();
I tried several Unit test, for those I could not find a number that does not work. Still of course, any improvements welcome!
PS: a shorter way to include minus sign in count with standard Java:
return n.toPlainString().length() - (n.scale() > 0 ? (n.scale() + 1) : 0);
Upvotes: 0
Reputation: 61
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);
Upvotes: 1
Reputation: 1074285
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);
}
}
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).
Upvotes: 4
Reputation: 1637
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
Upvotes: 1
Reputation: 339816
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
Upvotes: 30