Java Decimal Format parsing issue - java

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();
}

Related

Checking if a String has a fraction part in Java : Locale involved

I have a String value which can either hold a Long or a Double. The String may be Locale based.
So it may hold the following values:
11
11.00
15,25 (for Locale like Denmark where the decimal part is denoted by a comma instead of dot)
I want to do something only when it is a Double; in sense that it contains a fraction value. A fraction value of "00" is also a valid case.
if(string contains fraction){
// do something
}
Given above three examples, control should go inside if for 11.00 and 15,25 but not for 11.
How can I check this?
Please keep in mind that Locale is involved. So dot and comma may have different meaning for different Locale. So simple regex to find their occurrence won't work. For e.g. 11,00 is 1100 if Locale is Australia and thus is not a double. But 11,00 is a double if Locale is a European country like Denmark or Germany.
I need to find some solution using NumberFormat but not able to work it out.
I have Locale info. So I know if the String is of which Locale. Given that, how can I find if String has a fraction or not?
EDIT: Since you've edited your question stating you know the Locale, you can use it with NumberFormat.getNumberInstance(locale).parse(strValue) in combination with a regex for the comma and thousand separator. Here a test code:
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
class Main{
private static final Locale DUTCH = new Locale("nl","NL");
public static void main(String[] a){
test("11", Locale.ENGLISH);
test("11", DUTCH);
System.out.println();
test("11.00", Locale.ENGLISH);
test("11.00", DUTCH);
System.out.println();
test("11,00", Locale.ENGLISH);
test("11,00", DUTCH);
System.out.println();
test("15.123", Locale.ENGLISH);
test("15.123", DUTCH);
System.out.println();
test("15,123", Locale.ENGLISH);
test("15,123", DUTCH);
System.out.println();
test("something", Locale.ENGLISH);
test("something", DUTCH);
}
static void test(String val, Locale locale){
try{
DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale);
char decimalSep = symbols.getDecimalSeparator();
char thousandSep = symbols.getGroupingSeparator();
String escapedDecimalSep = decimalSep == '.' ? "\\." : decimalSep+"";
String escapedThousandSep = thousandSep == '.' ? "\\." : thousandSep+"";
String intRegex = "\\d+(" + escapedThousandSep + "\\d{3})*"; // Example ENGLISH: "\\d+(,\\d{3})*"
String doubleRegex = intRegex + escapedDecimalSep + "\\d+"; // Example ENGLISH: "\\d+(,\\d{3})*\\.\\d+"
NumberFormat format = NumberFormat.getInstance(locale);
Number number = format.parse(val);
if(val.matches(doubleRegex)){
double d = number.doubleValue();
System.out.println(val + " (in locale " + locale + ") is a double: " + d);
} else if(val.matches(intRegex)){
int i = number.intValue();
System.out.println(val + " (in locale " + locale + ") is an integer: " + i);
} else{
System.out.println("Unable to determine whether value " + val + " is an integer or double for locale " + locale);
}
} catch(ParseException ex){
System.out.println("Error occurred for value \"" + val + "\". Are you sure it's an integer or decimal?");
}
}
}
Try it online.
Here is the output:
11 (in locale en) is an integer: 11
11 (in locale nl_NL) is an integer: 11
11.00 (in locale en) is a double: 11.0
Unable to determine whether value 11.00 is an integer or double for locale nl_NL
Unable to determine whether value 11,00 is an integer or double for locale en
11,00 (in locale nl_NL) is a double: 11.0
15.123 (in locale en) is a double: 15.123
15.123 (in locale nl_NL) is an integer: 15123
15,123 (in locale en) is an integer: 15123
15,123 (in locale nl_NL) is a double: 15.123
Error occurred for value "something". Are you sure it's an integer or decimal?
Error occurred for value "something". Are you sure it's an integer or decimal?
With a regex you could do
Pattern decimalPattern = Pattern.compile("\\d+(,|\\.)\\d+{2}");
and then have
boolean isDecimal = decimalPattern.matcher(input).matches();
Regex:
\d+ one or more digits
(,|\\.) a decimal point or a comma
\d+ one or more digits again
Or you could do the splitting thing
String[] split = input.split("(,|\\.)");
boolean isDecimal = split.length > 1 && split[1].length() == 2;
You could use a loop to check if it have a comma or dot and then check with the if?
boolean IsADouble = false;
for (int i = 0; i < String.length(); i++) {
if (String.charAt(i) == ','|| String.charAt(i) == '.') {
IsADouble = true
}
}
And then you create the If to do something if its a double.
Hope it helped you :)

Is locale aware parsing for Numberformat possible in Android?

I have an Android application which does some basic mathematics.
Example
try {
NumberFormat nf = NumberFormat.getNumberInstance();
DecimalFormat df = (DecimalFormat) nf;
a = Float.parseFloat(vw3.getText().toString());
f = Float.parseFloat(vw5.getText().toString());
c = a / 100;
d = c * 1.036f;
e = f / 100;
g = e * 1.24f;
h = d + g;
String str1 = String.valueOf(df.format(h));
vw7.setText(str1);
} catch (NumberFormatException f) {
a = (0);
}
}
When the user is in the USA the calculations work fine and format fine. Well as you would expect. The 1,000.00 format where the grouping is by comma and separator is by decimal point. When a user is in France, the grouping is different and the separator is also different. Using the 1,000.00 example, in France the number would be formatted like this 1 000,00. A space is the grouping separator and the comma is the decimal separator. This causes a problem when you try and run a calculation and you will get a NumberFromatException (NFE). And I anticipated a NFE issue and catch it and replace the possible cause with the correct number. However, replacing any comma with a space and any period with a comma will also produce a NFE.
Example
try {
NumberFormat nf = NumberFormat.getNumberInstance();
DecimalFormat df = (DecimalFormat) nf;
a = Float.parseFloat(vw3.getText().toString().replace(",",""));
f = Float.parseFloat(vw5.getText().toString().replace(",",""));
c = a / 100;
d = c * 1.036f;
e = f / 100;
g = e * 1.24f;
h = d + g;
String str1 = String.valueOf(df.format(h));
vw7.setText(str1);
} catch (NumberFormatException f) {
a = (0);
}
}
EDIT - As suggested by Peter O. I have tried parsing the number with a locale aware means.
Example
NumberFormat.getNumberInstance().parse(string);
Or
NumberFormat df = NumberFormat.getNumberInstance();
String value = "10,40 €";
Number valueParsed = df.parse(value);
vw7.setText(valueParsed);
Will produce a "Bad Class" illegalargument.
I am looking for a solution to where I can do the calculations in an acceptable manner within the apps programming regardless of the locale and then later format the results to the locale. The question could be or is, do you force a locale for your calculations and then format the results for the locale?
If this is the code you are using and your strings will have the currency symbol. In this case € the EURO symbol.
Your example:
NumberFormat df = NumberFormat.getNumberInstance();
String value = "10,40 €";
Number valueParsed = df.parse(value);
vw7.setText(valueParsed);
so that value always has a currency symbol you need to use
NumberFormat df= NumberFormat.getCurrencyInstance();
instead of the .getNumberInstance(). This worked on my system which is currently set so that the € symbols is the default currency symbol. You will have to try it on a system that has the $ currency symbol in order to verify that it works there, also.

Java - Format numbers in scientific notation

I want to format this String:
String number = "3.4213946120686956E-9";
to this:
String number = "3.42E-9";
Rounding to 2 decimals. How can I achieve it?
Try new DecimalFormat("0.##E0"). Javadoc: DecimalFormat
Example:
public class Test {
private static java.text.DecimalFormat sf = new java.text.DecimalFormat("0.##E0");
public static void main(String[] args) {
System.out.println(sf.format(Double.parseDouble("3.4213946120686956E-9")));
}
};
Output: 3.42E-9
This is not the most elegant solution but you could:
EX: String number = "3.4213946120686956E-9";
if the String has a period and an E in it
split the string by the period, and get the first 2 characters after the period = 42
get the last 3 characters at the end = E-9
and then just add it back together with all the characters before the period = 3 + "." + 42 + E-9
Read about DecimalFormat.
String number = "3.4213946120686956E-9";
DecimalFormat format = new DecimalFormat("#.##E0");
System.out.println(format.format(new BigDecimal(number)));

Formating a number to two decimals without rounding up and converting to Double

I know this has been questioned alot of times but i tried all solutions in other threads and i cant find one that matches what i want ...
So i have one input something like this -9.22841 which is read as a String, what i want to do is to format this number to two decimals like this -9.23 without rounding it up and then converting it to double without losing this format...
I have tried many ways like String.format("%.2f",number) and the one below ...
String l = -9.22841
DecimalFormat df = new DecimalFormat("#,00");
String tmp =df.format(l);
double t = Double.parseDouble(tmp);
and this one:
String l = -9.22841
DecimalFormat df = new DecimalFormat("#.00");
String tmp =df.format(l);
double t = Double.parseDouble(tmp);
but everytime i try to convert to double in the String.format("%.2f",number) or DecimalFormat df = new DecimalFormat("#.00"); gives error converting to double
and when i do this :
DecimalFormat df = new DecimalFormat("#,00");
The output is wrong and is something like this -9.23 where it should be -9.22
Thanks for your time ...
You could just chop off the String two spaces after the decimal:
String number = "-9.22841";
String shorterNumber = number.substring(0, number.indexOf(".")+3);
double t = Double.parseDouble(shorterNumber);
System.out.println(t);
Thats what you want:
String number = "-9.22841";
DecimalFormat formatter = new DecimalFormat("0.00");
formatter.setRoundingMode(RoundingMode.DOWN);
number = formatter.format(Double.valueOf(number));
System.out.println(number);
The output will be:
-9,22
You can use bellow function:
import java.text.DecimalFormat;
import java.math.RoundingMode;
public static double formatValue(Double number) {
DecimalFormat df = new DecimalFormat("####0.00");
df.setRoundingMode(RoundingMode.DOWN);
return Double.parseDouble(df.format(number));
}
Input = 31.6227890 ,
OutPUT = 31.62
For someone looking full decimal handling:Kotlin
fun validateNumber(number: String): String {
return if (number.contains(".") && number.length > 3+number.indexOf("."))
number.substring(0, number.indexOf(".")+3)
else if (number.contains(".")){
number.substring(0, number.indexOf(".")+2)+"0"
}else{
"$number.00"
}
}

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

Categories

Resources