Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does DecimalFormat ".#" and "0.#" have different results on 23.0?

Why does java.text.DecimalFormat evaluate the following results:

new DecimalFormat("0.#").format(23.0)        // result: "23"

new DecimalFormat(".#").format(23.0)         // result: "23.0"

I would have expected the result to be 23 in both cases, because special character # omits zeros. How does the leading special character 0 affect the fraction part? (Tried to match/understand it with the BNF given in javadoc, but failed to do so.)

like image 791
MGn Avatar asked Mar 22 '16 15:03

MGn


People also ask

What does Java text DecimalFormat do?

DecimalFormat parses all Unicode characters that represent decimal digits, as defined by Character. digit() . In addition, DecimalFormat also recognizes as digits the ten consecutive characters starting with the localized zero digit defined in the DecimalFormatSymbols object.

How do you do 2 decimal places in Java?

Just use %. 2f as the format specifier. This will make the Java printf format a double to two decimal places. /* Code example to print a double to two decimal places with Java printf */ System.

What is decimal number format?

The "D" (or decimal) format specifier converts a number to a string of decimal digits (0-9), prefixed by a minus sign if the number is negative. This format is supported only for integral types. The precision specifier indicates the minimum number of digits desired in the resulting string.

Is DecimalFormat thread safe?

DecimalFormat isn't thread-safe, thus we should pay special attention when sharing the same instance between threads.


2 Answers

The second format seems to be invalid according to the JavaDoc, but somehow it parses without error anyway.

 Pattern:
         PositivePattern
         PositivePattern ; NegativePattern
 PositivePattern:
         Prefixopt Number Suffixopt
 NegativePattern:
         Prefixopt Number Suffixopt
 Prefix:
         any Unicode characters except \uFFFE, \uFFFF, and special characters
 Suffix:
         any Unicode characters except \uFFFE, \uFFFF, and special characters
 Number:
         Integer Exponentopt
         Integer . Fraction Exponentopt
 Integer:
         MinimumInteger
         #
         # Integer
         # , Integer
 MinimumInteger:
         0
         0 MinimumInteger
         0 , MinimumInteger
 Fraction:
         MinimumFractionopt OptionalFractionopt
 MinimumFraction:
         0 MinimumFractionopt
 OptionalFraction:
         # OptionalFractionopt
 Exponent:
         E MinimumExponent
 MinimumExponent:
         0 MinimumExponentopt

In this case I'd expect the behaviour of the formatter to be undefined. That is, it may produce any old thing and we can't rely on that being consistent or meaningful in any way. So, I don't know why you're getting the 23.0, but you can assume that it's nonsense that you should avoid in your code.

Update: I've just run a debugger through Java 7's DecimalFormat library. The code not only explicitly says that '.#' is allowed, there is a comment in there (java.text.DecimalFormat:2582-2593) that says it's allowed, and an implementation that allows it (line 2597). This seems to be in violation of the documented BNF for the pattern.

Given that this is not documented behaviour, you really shouldn't rely on it as it's liable to change between versions of Java or even library implementations.

like image 85
Software Engineer Avatar answered Oct 17 '22 06:10

Software Engineer


The following source comment explains the rather unintuitive handling of ".#". Lines 3383-3385 in my DecimalFormat.java file (JDK 8) have the following comment:

// Handle patterns with no '0' pattern character. These patterns
// are legal, but must be interpreted.  "##.###" -> "#0.###".
// ".###" -> ".0##".

Seems like the developers have chosen to interpret ".#" as ".0##", instead of what you expected ("0.#").

like image 33
MicSim Avatar answered Oct 17 '22 08:10

MicSim