Home > Back-end >  how to use DecimalFormat() to only output significant fractional digits
how to use DecimalFormat() to only output significant fractional digits

Time:04-14

Given 0.01835132889570552, the digits 1835 are significant (and might need to round up), and the digits 132889570552 are insignificant, so we don't want to put them into our user's input systems.

Any digits to the left of the decimal place are significant, and the minimum number of numerals to the right of the decimal place is 4.

We need to pass this test:

assertEquals("1,835.1329", formatInsignificantDigits(1835.13288957055));
assertEquals("183.5133", formatInsignificantDigits(183.513288957055));
assertEquals("18.3513", formatInsignificantDigits(18.3513288957055));
assertEquals("1.8351", formatInsignificantDigits(1.83513288957055));
assertEquals("0.1835", formatInsignificantDigits(0.183513288957055));
assertEquals("0.01835", formatInsignificantDigits(0.0183513288957055));
assertEquals("0.001835", formatInsignificantDigits(0.00183513288957055));
assertEquals("0.0001835", formatInsignificantDigits(0.000183513288957055));

Note that 183.5133 rounded up by 0.0001.

So the question is: How to write formatInsignificantDigits() without resorting to brute force or string surgery? I will answer my own question with a little brute force, and then we can see what others come up with.

CodePudding user response:

The math function to answer the question "How significant are my decimal digits?" is Math.log10(). So we convert our fractions into negative logarithms, and turn them into a positive number indicating how many zeros we need to pad. Then we add 4 to get all the digits, and we set that amount of precision:

public static @NonNull String formatInsignificantDigits(double input) {
    DecimalFormat df = new DecimalFormat();
    //noinspection NumericCastThatLosesPrecision
    int digits = 4   (int) -Math.log10(input);
    df.setMaximumFractionDigits(Math.max(digits, 4));
    return df.format(input);
}

Does anyone have a cleaner way to do this? Or a function of DecimalFormat that we overlooked?


And it turns out this algorithm converts -0.03997394115 to "0.04", because rounding up cascaded to the left, and then DecimalFormat lost the trailing zeros. That might not be acceptable in some circumstances...

CodePudding user response:

Not sure where this fits on the clean / brute-force continuum, but you could also make use of BigDecimal and MathContext to manage precision:

    public static String formatInsignificantDigits(double value) {
        DecimalFormat format = DecimalFormat.getInstance();
        format.setMaximumFractionDigits(Integer.MAX_VALUE);
        BigDecimal bigDecimal = new BigDecimal(value);
        int wholeDigits = Math.max(bigDecimal.precision() - bigDecimal.scale(), 0);
        return format.format(bigDecimal.round(new MathContext(wholeDigits   4)));
    }
  • Related