Pregunta

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?

¿Fue útil?

Solución

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.

Otros consejos

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top