I do not want to limit the number of significant digits in a BigDecimal. I only want to find the number of significant digits that number has.
Is there a way to do this without converting the number to string and count the number characters?
I do not want to limit the number of significant digits in a BigDecimal. I only want to find the number of significant digits that number has.
Is there a way to do this without converting the number to string and count the number characters?
I believe you want a combination of stripTrailingZeros
, precision
and scale
, as demonstrated here:
import java.math.*;
public class Test {
public static void main(String[] args) {
test("5000"); // 4
test("5000.00"); // 4
test("5000.12"); // 6
test("35000"); // 5
test("35000.00"); // 5
test("35000.12"); // 7
test("35000.120"); // 7
test("0.0034"); // 2
test("1.0034"); // 5
test("1.00340"); // 5
}
private static void test(String input) {
System.out.println(input + " => " +
significantDigits(new BigDecimal(input)));
}
private static int significantDigits(BigDecimal input) {
input = input.stripTrailingZeros();
return input.scale() < 0
? input.precision() - input.scale()
: input.precision();
}
}
The call to stripTrailingZeros
is required as otherwise it's entirely possible for a BigDecimal
to be stored in a "non-normalized" form. For example, new BigDecimal(5000)
has a precision of 4, not 1.
The call to scale()
is used to handle cases where the normalized form has trailing zeroes before the decimal point, but nothing after the decimal point. In this case, the scale will always be negative, and indicates the number of trailing zeroes.
EDIT: Cases with trailing zeroes but no decimal point are inherently ambiguous - there's no definite number of significant digits to "5000" for example. The above code treats all trailing zeroes before the decimal point as significant.
new BigDecimal(8.6)
isn't actually 8.6... it's the exact value of the nearest double
to 8.6
. If you expected to get 2, you should use new BigDecimal("8.6")
. Just print out new BigDecimal(8.6)
and you'll see what I mean. –
Aluminium new BigDecimal("5000.0").stripTrailingZeros().precision()
returns 1, but according to the accepted definitions you want it to return 5 (since you wouldn't add .0
to the end of a number unless it were significant). Also, the number of significant digits in "5000"
is ambiguous anyway. See "Significant figures" in Wikipedia. –
Approbation BigDecimal
isn't capable of distinguishing between, say, a "5000" with one significant digit and a "5000" with two, for example.) –
Approbation The following modification of Jon's answer returns the results that seem correct to me:
private static int significantDigits(BigDecimal input) {
return input.scale() <= 0
? input.precision() + input.stripTrailingZeros().scale()
: input.precision();
}
(Note that input.stripTrailingZeros().scale()
appears to always be negative in these tests.)
Also, as I noted above, BigDecimal
isn't capable of distinguishing between, say, a "5000" with one significant digit and a "5000" with two, for example. Furthermore, according to the definitions, "5000." (with a trailing decimal point) should have exactly four significant digits, but BigDecimal
isn't capable of handling that. (See http://en.wikipedia.org/wiki/Significant_figures for the definitions I'm using.)
Jon's answer is correct in most cases except exponential number:
private static int significantDigits(BigDecimal input) {
return input.scale() <= 0
? input.precision() + input.stripTrailingZeros().scale()
: input.precision();
}
let's say the input as 1.230000E17, the function returns 18 however the correct significant digits should be 7.
© 2022 - 2024 — McMap. All rights reserved.
precision()
alone isn't the answer, butnew BigDecimal(8.6).precision()
somehow returns50
, andnew BigDecimal(8.6).stripTrailingZeroes().precision()
also returns50
. – Filippo