NumberFormat.format() returns inconsistent values - java

I am parsing multiple files in parallel, and from times to times, the format() method will not return the right value.
Number parse = numberFormat.parse(val);
String format = numberFormat.format(parse);
format.equals(parse); //returns false sometimes
At first I thought it was due to the fact that the format method was not thread safe, but it was using a numberformat.clone() for each thread.
I also tried creating a new NumberFormat() for each thread, and also a ThreadLocal<NumberFormat>, with an initial value, and then calling the get() method, all with the same problem.
In the debugger, an evaluation of the expression always return the right value at the breakpoint.
I tried putting multiple lines String format = numberFormat.format(parse);, it turns out that randomly, one or several of the lines return a completely wrong value, and the other return the right one.
I'm 99% sure it's a thread issue, and a concurrent access is made to something, probably the numberFormat itself.
I might not have used the right way to make it thread safe, but in my understanding, using either clone() or new should get rid of that concern.
Any clues as to what is causing the issue, and how to fix it?
EDIT :
Here are two screen shots made with IntelliJ IDEA to showcase the issue :

Extend the NumberFormat class and synchronize the format method:
class SynchronizedNumberFormat extends NumberFormat {
public synchronized String format(Number number) {
return super.format(number);
}
//unimplemented methods...
}

There has never been any guarantee that a NumberFormat's format method will return exactly the same String as what you parsed the number from. In fact, many Strings can yield the same Number value.
First, consider trailing zeroes:
NumberFormat numberFormat = NumberFormat.getInstance();
Number parsed = numberFormat.parse("1.500000000000");
String formatted = numberFormat.format(parsed);
System.out.println(formatted); // prints 1.5
Second, NumberFormat doesn't parse a complete String like the Double.valueOf, Integer.valueOf, etc. It parses as much as it can from the String, and ignores trailing characters. The following are all valid operations that will parse successfully, without throwing a ParseException:
NumberFormat numberFormat = NumberFormat.getInstance();
numberFormat.parse("1.500000000000");
numberFormat.parse("1.5 ");
numberFormat.parse("1.5-----------");
numberFormat.parse("1.5helloworld");
All of the above calls return 1.5.

Related

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.
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.

Adding a leading zero to a large string in Java

I am currently making an auction program in Java, I am trying to work out deadlines, however my date keeps coming out as (7/04/2013 11:22), is there a way to use String.format to add a leading zero to this date when it is a one digit day?
String timeOne = Server.getDateTime(itemArray.get(1).time).toString()
It causes me a problem later on when I try to sub string it, and it is less than 17 characters long.
Thanks in advance, James.
#Leonard Brünings answer is the right way. And here's why your original code is the wrong way ... even if it worked.
The javadoc for Calendar.toString() says this:
"Return a string representation of this calendar. This method is intended to be used only for debugging purposes, and the format of the returned string may vary between implementations."
Basically you are using toString() for a purpose that the javadoc says you shouldn't. Even if you tweaked the output from toString(), the chances are that your code would be fragile. A change in JVM could break it. A change of locale could break it.
Simply use the SimpleDateFormat
import java.text.SimpleDateFormat;
Calendar timeOne = Server.getDateTime(itemArray.get(1).time)
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm")
System.out.println(sdf.format(timeOne.getTime()))

stringbuffer and "0&" causes truncation or escaping

Sorry for the unclear title but I don't even know what to call it, I'll just go ahead and explain what's happening.
I'm using a Stringbuffer to build an URL. It looks like this:
http://maps.googleapis.com/maps/api/geocode/json?latlng=49.0516736,8.38891840&sensor=false
I encountered this behavious when comparing this string in a Unit-test to the actual result of the method.
And this is the assertion-error I'm getting:
latlng=49.0516736[,8.38891840]&sensor=false> but was:<...on?latlng=49.0516736[,8.3889184]&sensor=false
The emphasis is on the character sequence 0]& and 4]& right before sensor=false
IF I remove the zero before the & the test goes green.
then the created string looks like this:
latlng=49.0516736,8.3889184&sensor=false
so ... just as expected.
It's not the problem, that the 0 itself gets truncated and test would fail - I've proved that my code is doing what it's supposed to (when I remove the zero), but I want to know what is happening here.
0& must be some kind of indication for array-access or some kind of escaping. I don't know.
Anyone any idea what's causing this behaviour?
Edit:
Here's the code I'm using
StringBuffer s = new StringBuffer( grailsApplication.config.geocodingurl.toString() )
s.append(coordinates.latitude)
s.append(",")
s.append(coordinates.longitude)
s.append("&sensor=false")
return s.toString()
There is a formatting/padding issue when converting double into String.
What you are doing is probably using StringBuilder#append(double) which in the end calls Double#toString().
See the javadoc of those methods and find out how double values are converted to String.
Alternatively, if you want to have control over your code, use NumberFormat or it's subclasses.

Cleanest way to format a String

I'm trying to format a phone number which is stored without formatting in a database.
Now currently I just use substring and String concatination to form the formatted String but I'm looking for a cleaner/faster/less memory intensive method. (and I don't mean just using a StringBuilder).
I looked at String.format but that only takes a list of parameters (as in ...) and not a chararray.
I'll toss in my 2 cents after some lookup:
import java.swing.text.MaskFormater;
try {
MaskFormatter formatter = new MaskFormatter("+AA AAA AA AA AA");
formatter.setValueContainsLiteralCharacters(false);
System.err.println(formatter.valueToString("31987365414"));
} catch (ParseException e) {
}
If you want to build a String from char array(s) plus some things (chars, strings, whatever) between them, then StringBuilder is definitely the right way to go, if you don't want to simply concatenate. An important point is to initialize the builder with enough initial capacity so that it doesn't need to reallocate its internals while building.

Java DecimalFormat problem

I would like to format numbers of type double with a German locale in Java. However something goes wrong since the output of the following code is : 0.0
package main;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
public class Test {
private static String decimal2Format = "000.000";
public static void main(String args[]){
DecimalFormat format = (DecimalFormat)NumberFormat.getInstance(new Locale("de"));
double value = 0;
try {
format.applyPattern(decimal2Format);
value = format.parse("0.459").doubleValue();
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(value);
}
}
Do you have any ideas what is wrong or missing?
Thanks,
atticus
You're trying to parse using a pattern which will expect a comma (as it's in German) but you've given it a period ("0.459"). It looks like DecimalFormat stops parsing when it sees a character it doesn't understand. If you change it to "0,459" you'll see it parse correctly and then output "0.459". (I'm not sure whether System.out.println uses the system default locale, in which case it might print "0,459" depending on your locale.)
Note that you haven't tried to format the number at all in this code - only parse a number. If you want to format it, call format. The double itself doesn't have an associated format - it's just a number. It's not like parsing using a particular formatter returns a value which retains that format.
Here's code which will perform actual formatting of a double value:
DecimalFormat format = (DecimalFormat)NumberFormat.getInstance(new Locale("de"));
double value = 0.459;
String formatted = format.format(value);
System.out.println(formatted); // Prints "0,459"
EDIT: Okay, so it sounds like you're converting it from one format to another (from US to European, for example). That means you should probably use two different DecimalFormat objects - you could switch the locale between calls, but that sounds a bit grim to me.
I believe one way to parse and detect errors is to use the parse overload which takes a ParsePosition as well. Set the position to 0 to start with, and afterwards check that it's at the end of the string - if it isn't, that means parsing has effectively failed. I find it odd that there isn't a method which does this automatically and throws an exception, but I can't see one...
You may also want to set the parser to produce a BigDecimal instead of a double, if you're dealing with values which are more logically decimal in nature. You can do this with the setParseBigDecimal method.

Categories

Resources