Java DecimalFormat formatting zero - java

I am trying to format my (usually large) numeric output with space as thousands separator using the following code:
final String PATTERN = "#,##0";
int value1 = Integer.MAX_VALUE;
int value2 = 0;
DecimalFormatSymbols dfs = new DecimalFormatSymbols();
dfs.setGroupingSeparator(' ');
DecimalFormat df = new DecimalFormat(PATTERN, dfs);
System.out.println("`" + df.format(value1) + "`");
System.out.println("`" + df.format(value2) + "`");
However, which leaves me very confused, the output is:
`2 147 483 647`
``
Please, what is the proper pattern to achieve my desired result:
`2 147 483 647`
`0`

As the good people have already responded in the comments, the pattern is correct and produces the desired result.
The problematic part has been just one step away. Assuming code in the question, then if df.format(getNumber()); fails in #getValue(), the #format() is never executed, which (in my code) eventually resulted in empty string in the user interface and my confusion.

Related

Java Decimal Format parsing issue

public class NumFormatTest
{
public static void main(String[] args) throws ParseException
{
String num = "1 201";
DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.FRANCE);
System.out.println("Number Before parse: "+num);
double dm = df.parse(num).doubleValue();
System.out.println("Number After parse: "+dm);
}
}
Output:
Number Before parse: 1 201
Number After parse: 1.0
Expected Output:
Number Before parse: 1 201
Number After parse: **1201**
Can any please help me understand why parse is not able to convert a FRENCH locale formatted string (1 201) to normal double value (1201.0)?
There are two kinds of spaces. The "normal" space character (No. 32 - HEX 0x20) and the non-breaking space (NBSP) (No. 160 - HEX 0xA0).
The French locale expects the whitespace character between the digits to be the non breaking space! You can help yourself with this line of code:
String num = "1 201";
num = num.replaceAll(" ", "\u00A0"); // '\u00A0' is the non breaking whitespace character!
This way your code will work like expected. Please note that if you format a double into a String with French locale the resulting whitespace character will be the NBSP too!!!
DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.FRENCH);
System.out.println(df.format(1201.1));
// This will print "1 202,1" But the space character will be '\u00A0'!
You can use
String num = "1 201";
DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.FRANCE);
System.out.println("Number Before parse: "+num);
DecimalFormatSymbols symbols = df.getDecimalFormatSymbols();
symbols.setGroupingSeparator(' ');
df.setDecimalFormatSymbols(symbols);
double dm = df.parse(num).doubleValue();
System.out.println("Number After parse: "+dm);
Expected Output:
Number Before parse: 1 201
Number After parse: 1201.0
Actually, Java is using the character unbreakable space (\u00a0) to parse French numbers.
Thus, the following code actually works:
String num = "1\u00a0201";
double dm = df.parse(num).doubleValue();
System.out.println("Number After parse: " + dm);
See #ParkerHalo answer which provide more details.
This seems to work.
public double parse(String decimalAsText) {
NumberFormat decimalFormat = NumberFormat.getNumberInstance(Locale.FRANCE);
decimalAsText = decimalAsText.replace(' ', '\u00a0');
ParsePosition pp = new ParsePosition(0);
Number n = decimalFormat.parse(decimalAsText, pp);
return n.doubleValue();
}

Parsing String to Double including scientific notation E

I use Choco constraint solver to solve a problem.
I use RealVariables x, y and z.
The solution looks like this
x[3.9999999999999996, 4.000000000000001]
y[-3.1434555694057773E-162, 3.1434555694057773E-162]
z[-3.1434555694057773E-162, 3.1434555694057773E-162]
And I try to parse the solutions to double variables.
Here is my code:
String X = s.getVar(x).toString();
String Y = s.getVar(y).toString();
String Z = s.getVar(z).toString();
Matcher match = Pattern.compile("-?\\d+(\\.\\d+)?").matcher(X);
while(match.find())
{
double d = Double.parseDouble(match.group());
System.out.println("value= " + d);
}
match = Pattern.compile("-?\\d+(\\.\\d+)?").matcher(Y);
while(match.find())
{
DecimalFormat format = new DecimalFormat("0.00");
Double d = Double.parseDouble(match.group());
System.out.println("value= " + format.format(d));
}
match = Pattern.compile("-?\\d+(\\.\\d+)?").matcher(Z);
while(match.find())
{
double d = Double.parseDouble(match.group());
System.out.println("parsed: " + d);
}
The output is:
parsed: 3.9999999999999996
parsed: 4.000000000000001
parsed: -3,14
parsed: -162,00
parsed: 3,14
parsed: -162,00
parsed: -3.143455569405777
parsed: -162.0
parsed: 3.143455569405777
parsed: -162.0
Which means, -162 is parsed like another double. How do I prevent this? How can I parse a String to Double using scientific notation?
Does this happen because E-162 is too long for Double?
You need to change your pattern to take account of the exponant:
Matcher match = Pattern.compile("-?\\d+(\\.\\d+)?(E-?\\d+)?").matcher(X);
Note: The capturing groups are not needed here, you can replace them by non-capturing groups (?:...).
Since you will use the same regex several times, it is better to define the pattern once and for all, instead of repeating Pattern.compile("-?\\d+(\\.\\d+)?(E-?\\d+)?"). You can use this:
static Pattern pattern = Pattern.compile("-?\\d+(?:\\.\\d+)?(?:E-?\\d+)?");
....
Matcher match = pattern.matcher(X);
....
match = pattern.matcher(Y);
....
match = pattern.matcher(Z);
Double#parseDouble parses doubles from Strings and it parses the exponet aswell.
System.out.println(Double.parseDouble("1.2345e5"));
--> 123450.0
Example: https://ideone.com/ACTVQT
To catch all numbers in Java from regex use this pattern
"-?\\d+(\\.\\d+)?(E-?\\d+)?(E\\+?\\d+)?(E?\\d+)?(e-?\\d+)?(e\\+?\\d+)?(e?\\d+)?"
I have tried this regex on following strings and all of them returned true
"2546546516",
"-2546546516",
"2546546516.1241546",
"-2546546516.415441",
"2.5465E+09",
"-2.5465E+09",
"2.5465E-09",
"-2.5465E-09",
"2.5465e+09",
"-2.5465e+09",
"2.5465e-09",
"-2.5465e-09",
"445.4e2",
"-445.4e2"
Note: This answer was inspired by Casimir's answer
PS. Edits are welcome

Formatting currency in Android using wrong decimal separator

I got a bug report from a Swedish user saying that our Swedish currency was using the wrong decimal separator.
NumberFormat enUS = NumberFormat.getCurrencyInstance(Locale.US);
NumberFormat enGB = NumberFormat.getCurrencyInstance(Locale.UK);
NumberFormat svSE = NumberFormat.getCurrencyInstance(new Locale("sv", "SE"));
double cost = 1020d;
String fmt = "en_US: %s en_GB %s sv_SE %s";
String text = String.format(fmt, enUS.format(cost), enGB.format(cost), svSE.format(cost));
Log.e("Format", text);
> Format﹕ en_US: $1,020.00 en_GB £1,020.00 sv_SE 1 020:00 kr
They say that the format should be "1 020,00 kr". When I inspect the format object, it looks like it has decimalSeparator of "," in the symbols table, but a "monetarySeparator" of ":".
Does anyone know if : is actually correct, whether this is a bug in Android/java, or any sort of workaround?
It's like your user says: In Swedish thousand separator is white space " " and decimal separator is comma "," and currency symbol "kr" (Krona). So colon ":" is definitely wrong.
You can check it here too: http://www.localeplanet.com/java/sv-SE/
What Java version are you using? It works well on my desktop 1.6.0_13
-- update --
It seems that on Android there's a bug, but you can go around the bug by using the DecimalFormatSymbols like this:
DecimalFormat svSE = new DecimalFormat("#,###.00");
DecimalFormatSymbols symbols = new DecimalFormatSymbols(new Locale("sv", "SE"));
symbols.setDecimalSeparator(',');
symbols.setGroupingSeparator(' ');
svSE.setDecimalFormatSymbols(symbols);
This prints the correct separators in Android as well.

Formatting Double with two dp

From my decimalForm method below, i want to convert double value 7777.54 to 7 777,54 but i am getting 7 777 54. what have missed ? result should be 7 777,54
public static String decimalForm(double value){
DecimalFormat df = new DecimalFormat("#,###,###.00");
String formatted_value = df.format(value).replaceAll(",", " ").replace(".", ",");
return formatted_value;
}
This works for me:
DecimalFormat df = new DecimalFormat("#,###,###.00");
String formatted_value = df.format(value).replaceAll("\\.", " ");
In fact i tried to print out df.format(value) and, with value=95871 i got 95.871,00
you can use
String formatted_value = String.format("$%.2f", value);
In the pattern #,###,###.00, . is the decimal separator and , is the group separator. The character used for this two separators depends on your locale.
For example, if you are french, df.format(value) will equals to 7 777,54.
This is no String.replace version
DecimalFormat df = new DecimalFormat("#,###,###.00", new DecimalFormatSymbols(Locale.FRANCE));
String s = df.format(7777.54);
System.out.println(s);
output
7 777,54

Java: replacing characters in a String

I have a String that represents a time value and is stored in the following format:
1:31:25
I would like to replace the colons and change the format to:
1h 31m 25s
What function in Java will let me replace the first two colons with 'h ' and 'm ', and the end of the string with 's'.
You could do something like this:
String[] s = myString.split(":");
String.format("%sh %sm %ss", s);
Or even compact!
String.format("%sh %sm %ss", myString.split(":"));
String time = "1:31:25";
String formattedTime = time.replaceFirst(":","h ").replaceFirst(":","m ").concat("s");
String input = "1:31:25";
String[] tokens = input.split(":");
String output = tokens[0] + "h " + tokens[1] + "m " + tokens[2] + "s";
Repeated use of the String.replaceFirst() method would help you here.
Simply replace your first ':' with the 'h', then apply again for 'm' etc.
There are additional options, which may be more appropriate/robust etc. depending on your circumstances.
Regular expressions may be useful here, to help you parse/split up such a string.
Or given that you're parsing/outputting times, it may also be worth looking at SimpleDateFormat and its ability to parse/output date/time combinations.
In fact, if you're storing that date as a string, you may want to revist that decision. Storing it as a date object (of whatever variant) is more typesafe, will protect you against invalid values, and allow you to perform arithmetic etc on these.
String[] timeStr = "1:31:25".split(":");
StringBuffer timeStrBuf = new StringBuffer();
timeStrBuf.append(timeStr[0]);
timeStrBuf.append("h ");
timeStrBuf.append(timeStr[1]);
timeStrBuf.append("m ");
timeStrBuf.append(timeStr[2]);
timeStrBuf.append("s");
You can use a regular expression and substitution:
String input = "1:31:25";
String expr = "(\\d+):(\\d+):(\\d+)";
String substitute = "$1h $2m $3s";
String output = input.replaceAll(expr, substitute);
An alternative is to parse and output the String through Date:
DateFormat parseFmt = new SimpleDateFormat("HH:mm:ss");
DateFormat displayFmt = new SimpleDateFormat("H'h' mm\'m' ss's'");
Date d = parseFmt.parse(input);
output = displayFmt.format(d);
Use split()
String s = "1:31:25";
String[] temp = s.split(":");
System.out.println(s[0]+"h"+" "+s[1]+"m"+" "+s[2]+"s");

Categories

Resources