DrunkenPope
DrunkenPope

Reputation: 576

NumberFormat doesn't crash with 2 decimal separators

I have a question regarding the behavior of the NumberFormat:

When I want to translate/parse a formatted String into a Number, then I would like to use NumberFormat, since it provides me with nice presets for thousand and decimal separators. Additionally I would like it to crash, if the provided String is not a valid Number.

An example:

// works as expected
String testInput1 = "3,1415";
NumberFormat germanNumberFormat = NumberFormat.getInstance(Locale.GERMANY);
Number number1 = germanNumberFormat.parse(testInput1);
System.out.println(number1); // prints 3.1415

// does not work as expected, cuts off the number after the 2nd decimal
// separator, expected it to crash with java.lang.NumberFormatException:
// multiple points
String testInput2 = "3,14,15";
Number number2 = germanNumberFormat.parse(testInput2);
System.out.println(number2); // prints 3.14

I currently use Double.parseDouble(String s), to have this additional behavior:

// crashes with java.lang.NumberFormatException: multiple points
double number2WithError = Double.parseDouble(testInput2.replace(",", "."));

Is there a way I can use NumberFormat to have my required/expected behavior besides writing my own wrapper class that does some additional checks on e.g. multiple decimal separators?


Also I'm aware that the JavaDoc of the used parse(String source) method of NumberFormat says:

Parses text from the beginning of the given string to produce a number. The method may not use the entire text of the given string.

See the {@link #parse(String, ParsePosition)} method for more information on number parsing.

and parse(String source, ParsePosition parsePosition):

Returns a Long if possible (e.g., within the range [Long.MIN_VALUE, Long.MAX_VALUE] and with no decimals), otherwise a Double. If IntegerOnly is set, will stop at a decimal point (or equivalent; e.g., for rational numbers "1 2/3", will stop after the 1). Does not throw an exception; if no object can be parsed, index is unchanged!

This doesn't tell me though why the method behaves this way. What I get from these is that they can parse only parts of the String (what they obviously do here) and probably just start parsing at the beginning (start position) until they find something they can't deal with.


I didn't find an existing question covering this, so if there is already one, please feel free to close this post and please link to it.

Upvotes: 0

Views: 253

Answers (1)

John Bollinger
John Bollinger

Reputation: 180978

NumberFormat.parse(String) is behaving exactly as documented:

Parses text from the beginning of the given string to produce a number. The method may not use the entire text of the given string.

(Emphasis added)

You ask:

Is there a way I can use NumberFormat to have my required/expected behavior besides writing my own wrapper class that does some additional checks on e.g. multiple decimal separators?

You cannot provide a format that will make NumberFormat.parse() throw an exception for input with only an initial substring that can be parsed according to the format. You can, however, use NumberFormat.parse(String, ParsePosition) to determine whether the whole input was parsed, because the parse position argument is used not only to indicate to the method where to start, but also for the method to say where it stopped. That would be a lot better than implementing format-specific extra checks. Example:

ParsePosition position = new ParsePosition(0);
Number result = format.parse(input, position);

if (position.getIndex() != input.length()) {
    throw new MyException();
}

Additionally, you write:

This doesn't tell me though why the method behaves this way.

It behaves that way because sometimes parsing the initial portion of the input is exactly what you want to do. You can build stricter parsing on top of more relaxed parsing, as shown, but it's much more difficult to do it the other way around.

Upvotes: 1

Related Questions