-1

I have a situation where I need to preserve the number of decimal places for a number when formatting to a String using DecimalFormat, specifically for trailing zeros. I need to use DecimalFormat because I also need to avoid scientific notation for large numbers, and as seen here, this is the best way to do so. However, as you can see in that post, the code provided also removes trailing zeros, whereas I would like to preserve them.

1.00 -> "1.00"
1.000 -> "1.000"

I've seen a lot of questions that involve a fixed number of decimal places, but in my situation I need to account for a variable amount of decimal places. I looked into counting the number of decimal places, but since my input can also be a Double (in addition to BigDecimal), there appears to be no reliable way to count the digits after the decimal point for all numbers. Double.toString() does not work since Doubles break into exponential notation for very small and very big numbers. See here for more info regarding why it is difficult to count the number of decimal places in a double.

adiadi
  • 57
  • 1
  • 8

2 Answers2

3

"Preserve" trailing zeros?

A double value doesn't know how many trailing zeroes you want to see. It is just a number, and 1.00 and 1.000 are the same number, i.e. the number 1. What you are asking cannot be done with a double value.

Now, BigDecimal does remember the number of trailing zeroes, so if you want to print a BigDecimal value, retaining the scale of the number, but ensuring it never prints in scientific notation, don't use a DecimalFormat, but instead use toPlainString():

Returns a string representation of this BigDecimal without an exponent field.


UPDATE

If you want to print a double value with as many decimal fraction digits as needed (i.e. no trailing zeroes), and want to make sure it never prints in scientific notation, use a DecimalFormat with very high MaximumIntegerDigits and very high setMaximumFractionDigits.

"Very high" means values exceeding the range of a double, so 999 is a good "round" number, though 330 would be high enough.

Test

DecimalFormat fmt = new DecimalFormat("0");
fmt.setMaximumIntegerDigits(330);
fmt.setMaximumFractionDigits(330);

System.out.println("0.0123400  = " + 0.0123400               + "   = " + fmt.format(0.0123400));
System.out.println("123400.00  = " + 123400.00                + "  = " + fmt.format(123400.00));
System.out.println("NaN        = " + Double.NaN          + "       = " + fmt.format(Double.NaN));
System.out.println("-INFINITY  = " + Double.NEGATIVE_INFINITY  + " = " + fmt.format(Double.NEGATIVE_INFINITY));
System.out.println("+INFINITY  = " + Double.POSITIVE_INFINITY + "  = " + fmt.format(Double.POSITIVE_INFINITY));
System.out.println("MIN_NORMAL = " + Double.MIN_NORMAL               + " = " + fmt.format(Double.MIN_NORMAL));
System.out.println("MIN_VALUE  = " + Double.MIN_VALUE + "                = " + fmt.format(Double.MIN_VALUE));
System.out.println("MAX_VALUE  = " + Double.MAX_VALUE               + "  = " + fmt.format(Double.MAX_VALUE));

Output

0.0123400  = 0.01234   = 0.01234
123400.00  = 123400.0  = 123400
NaN        = NaN       = �
-INFINITY  = -Infinity = -∞
+INFINITY  = Infinity  = ∞
MIN_NORMAL = 2.2250738585072014E-308 = 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014
MIN_VALUE  = 4.9E-324                = 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049
MAX_VALUE  = 1.7976931348623157E308  = 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • Unfortunately, the input can be a double as well. So if I tried the BigDecimal approach that you outlined with the value 1.2 here is the output: new BigDecimal(1.2).toPlainString() results in 1.1999999999999999555910790149937383830547332763671875' – adiadi Oct 26 '17 at 19:09
  • @adiadi Don't use the [`BigDecimal(double)`](https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html#BigDecimal-double-) constructor. Use [`BigDecimal.valueOf(double)`](https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html#valueOf-double-) instead. The javadoc even tells you so: *This is generally the preferred way to convert a `double` (or `float`) into a `BigDecimal`, as the value returned is equal to that resulting from constructing a `BigDecimal` from the result of using `Double.toString(double)`.* – Andreas Oct 26 '17 at 19:18
  • actually `1.0`, `1.000` and `1` are technically not the same value when represented in floating point binary such as `double` and `float`. –  Apr 04 '18 at 16:14
  • @JarrodRoberson `1.0`, `1.000` and `1` are *exactly* the same value in a floating point binary such as `double` and `float`, e.g. as a `float` all three are the bytes `3f 80 00 00`. They are not the same in a `BigDecimal`, but are surely the same as a floating point value. – Andreas Apr 04 '18 at 16:19
0

Either double (no precision, always approximating values), or BigDecimal.

Use BigDecimal as new BigDecimal("2.00") defining the correct scale (precision) of two digits. You can set the scale programmatically.

For database and calculation (like financial) BigDecimal is fine.

The scientific representation on output can be avoided in BigDecimal.toPlainString().

For double one could format with

s = String.format("%10.2f", 13.5789); // "   13.58"

All this is with a decimal point and no thousands separators.

Internationalized (localized) software will use a MessageFormat with a Locale (explicit or the default platform locale).

Locale.setDefault(new Locale("bg", "BG"));
s = MessageFormat.format("Number: {0, number, #.##}, "
            + "amount: {1, number, currency}",
            42.125, 19.99);
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • Regarding using BigDecimal: I am unable to determine the correct scale for an input that is a double. In all other cases I can do this, but if it is a double I have no way of doing it because a double, as you said, has an approximate value. Say an input comes in as 1.200. I need to convert this to a string. How can I do this using DecimalFormat without hardcoding the scale? – adiadi Oct 26 '17 at 19:17
  • 1
    @adiadi You can't. As a `double` value, `1.200` is *exactly* the same number as `1.2`, which you **already know**, since the [answer](https://stackoverflow.com/a/3334190/5221149) in the second link in your question have already told you so, so why are you asking? – Andreas Oct 26 '17 at 19:27
  • Either `new BigDecimal("1.200")` (textual input) or `new BigDecimal(1.2).setScale(3)` (yes programmatic as 1.2 == 1.200). – Joop Eggen Oct 26 '17 at 20:00
  • @adiadi The problem is nothing to do with the approximate nature of double. Double does not have any bits set aside to remember a number of trailing zeros. 1.0, 1.00, 1.000, ... are all represented as 0x3ff0_0000_0000_0000 in double, with no rounding error. – Patricia Shanahan Oct 27 '17 at 09:05