Recommended way to format numbers in a locale aware way? - java

Lets assume we have one million.
In English it should be formatted as 1,000,000 in German it should be 1.000.000.

Using NumberFormat class:
For English:
NumberFormat nf_us = NumberFormat.getInstance(Locale.US);
String number_us = nf_us.format(1000000);
For German:
NumberFormat nf_ge = NumberFormat.getInstance(Locale.GERMAN);
String number_ge = nf_ge.format(1000000);

You can use NumberFormat.
Android documentation is quite clear on it.

You can achieve this with using the NumberFormat class, this also allows you to parse Strings into a local aware number.
NumberFormat formatter = NumberFormat.getInstance(Locale.GERMAN);
String localeFormattedNumber = formatter.format(1000000);

Related

Convert double value to contain decimal seperator and only two digits after comma in groovy

I would like to convert a number such as the following:
2937998.7397271004
to look like this:
2.937.998,73
My attempt of achieving this looks as following, but the result still looks the same:
DecimalFormat decimalFormat = new DecimalFormat("###.###.###.###,00");
def val = decimalFormat.format(cell.getNumericCellValue().doubleValue())
return val
One way to do is to use a locale which supports your formatting pattern. For example Locale.GERMAN is in line with what you are expecting
DecimalFormat df = new DecimalFormat("###,###.00",
DecimalFormatSymbols.getInstance(Locale.GERMAN));
df.format(2937998.7397271004); // 2.937.998,74
You should definitely use Locale for this issue. And in your case formatting is german:
import java.text.NumberFormat
NumberFormat numberFormat = NumberFormat.getInstance(Locale.GERMAN);
println numberFormat.format(2937998.7397271004)
Although I am not familiar with groovy as far as I know it still utilizes the java libraries.
You cannot utilize multipled .'s on a standard DecimalFormat as can be seen in the documentation for the java class: Documentation.
So the following line of code needs to changed to:
DecimalFormat decimalFormat = new DecimalFormat("###,###,###,###.00");
Which will respond with 2.937.998,74 utilizing the provided value of 2937998.7397271004.
If you want to do it as 2.937.998,74 you can, again at least in java, do the following.
DecimalFormat decimalFormat = new DecimalFormat("###,###,###,###.00");
DecimalFormatSymbols custom = new DecimalFormatSymbols();
custom.setDecimalSeparator(',');
custom.setGroupingSeparator('.');
decimalFormat.setDecimalFormatSymbols(custom);
String val = decimalFormat.format(cell.getNumericCellValue().doubleValue());
Or use the locale as other aswers sugest.

Number Format (Comma Separation) as per locale

I have a requirement to show a number value like 123456789.905 in the following format 123,456,789.90. But the comma separation changes depending on the locale selected in the phone (as if with US English selected comma separation is 3 places and if India English is selected it is like 12,34,56,789.90).
How can I format my Double?
So, java.text.NumberFormat doesn't slove the problem, unfortunately, but com.ibm.icu.text.NumberFormat does.
You can use this:
Double d = 123456789.905;
com.ibm.icu.text.NumberFormat format = com.ibm.icu.text.NumberFormat.getNumberInstance(new Locale("en", "in"));
format.setMinimumFractionDigits(2);
format.setMaximumFractionDigits(2);
System.out.println(format.format(d));
This outputs: 12,34,56,789.90.
For the generic case, use java.text.NumberFormat:
NumberFormat nf = NumberFormat.getInstance();
String formatted = nf.format(yourDoubleValue);
By default getInstance() returns a NumberFormat that is configured as appropriate for the current Locale. You can change the configuration yourself, too.
The "comma separation" is called "grouping".
For the specific case of grouping in an Indian currency format, see: Displaying Currency in Indian Numbering Format
Try this one:
try {
Locale l = Locale.getDefault();
NumberFormat nf = NumberFormat.getInstance(l);
String formato = NumberFormat.getInstance().format(your_data);
} catch (Exception e) {
e.printStackTrace();}
Use NumberFormat, which helps you to format and parse numbers for any locale.
Your code can be completely independent of the locale conventions for
decimal points, thousands-separators, or even the particular decimal
digits used, or whether the number format is even decimal.
Locale fmtLocale = Locale.getDefault();
NumberFormat formatter = NumberFormat.getInstance(fmtLocale);
formatter.format(your_number);
Hm, I have not found for any locale in NumberFormat.getAvailableLocales() a format with only two digits between grouping signs (for example for new Locale("en", "IN")). So I think you have to use DecimalFormat-pattern like this:
DecimalFormat df = new DecimalFormat("##,##,##,##,##.###");
System.out.println(df.format(123456789.905));
// Output: 1.23.45.67.89,905
It is not exactly the same since DecimalFormat is not able to have varying counts of grouping sizes, but maybe this is acceptable for you.
NumberFormat nf = NumberFormat.getInstance(Locale.getDefault());
double value = nf.parse(iValue).doubleValue();

NumberFormat.parse() fails for some currency strings

I have a simple EditText, which allows the user to enter a number such as 45.60 (example for American Dollar). I then format this number using the following method:
public String format() {
NumberFormat formatter = NumberFormat.getCurrencyInstance(Locale.getDefault());
return formatter.format(amount.doubleValue());
}
And on my Android phone, the language is set to English (United States) - hence the Locale.getDefault() should return the US locale (and it does).
Now the edit text is correctly updated to: $45.60 (hence formatting the entered number works).
However if I attempt to parse the above String "$45.60" using the following method:
NumberFormat numberFormat = NumberFormat.getInstance(Locale.getDefault());
Number result = numberFormat.parse("$45.60");
It fails with:
java.lang.IllegalArgumentException: Failed to parse amount $45.60 using locale en_US.
If I set my phone to English/ UK, formatting this "45.60" to "£45.60" works correctly (as for US), however parsing "£45.60" fails, just as it does for the above US sample.
However, if I set my phone to German (Germany), formatting "45,60" to "45,60€" works correctly, AND parsing "45,60€" works correctly as well!
The only difference I see between those three currencies: The Euro is appended to the amount, while the Dollar and the Pound are prepended to the amount.
Does anyone have an idea, why the same code works for Euro, but not for Pound and Dollar? Am I missing something?
I also created a unit test, to reproduce the issue:
public void testCreateStringBased() throws Exception {
// For German locale
CurrencyAmount amount = new CurrencyAmount("25,46€", Locale.GERMANY);
assertEquals(25.46, amount.getAsDouble());
// For French locale
amount = new CurrencyAmount("25,46€", Locale.FRANCE);
assertEquals(25.46, amount.getAsDouble());
// For US locale
amount = new CurrencyAmount("$25.46", Locale.US);
assertEquals(25.46, amount.getAsDouble());
// For UK locale
amount = new CurrencyAmount("£25.46", Locale.UK);
assertEquals(25.46, amount.getAsDouble());
}
CurrencyAmount basically wraps the code I posted for parsing currency strings, except that it takes the given locale instead of the default locale. In the above example, the test succeeds for the GERMANY and FRANCE locale but fails for US and UK locale.
Since the answers that have been suggested thus far, did not completely solve the problem, I took a painfully amateurish approach:
String value = "$24,76"
value = value.replace(getCurrencySymbol(locale), StringUtils.EMPTY);
NumberFormat numberFormat = NumberFormat.getInstance(locale);
Number result = numberFormat.parse(value);
So now I simply strip the String value off it's currency symbol... This way I can process everything I want, such as: 45.78 or 45,78 or $45.78 or 45,78€ ....
Whatever the input, the currency symbol is simply stripped and I end up with the plain number. My unittests (see OP) now complete successfully.
If anyone comes up with something better, please let me know.
Try following:
NumberFormat numberFormat = new DecimalFormat("¤#.00", new DecimalFormatSymbols(Locale.UK));
numberFormat.parse("£123.5678");
¤ - currency sign, expects matches with currency symbol by Locale.
other pattern symbols you can see by following link http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html
Try NumberFormat.getCurrencyInstance().parse() instead of NumberFormat.getInstance().parse().
You must know the locale of the string you wish to parse in order to have a locale-aware parser. The GBP string parse to a numeric ONLY when the NumberFormat's locale is en_GB; there is no such thing as a "universal" parser.
For example, how does the string "12.000" parse? For en-us, the answer is twelve; for de-de, the answer is twelve-thousand.
Always use NumberFormat.getCurrencyInstance( java.util.Locale ) to parse currency amounts.
I'm using below adapted from https://dzone.com/articles/currency-format-validation-and
import java.math.BigDecimal;
import org.apache.commons.validator.routines.*;
BigDecimalValidator currencyValidator = CurrencyValidator.getInstance();
BigDecimal parsedCurrency = currencyValidator.validate(currencyString);
if ( parsedCurrency == null ) {
throw new Exception("Invalid currency format (please also ensure it is UTF-8)");
}
If you need to insure the correct Locale is being used per user look at
Change locale on login
Sorry, but any answer provided are misleading. This is what I would call a BUG in Java.
An example like this explains it better. If I want to print a value in EUR using Locale.US and then I parse it again, it fails unless I specify on the DecimalFormat the currency (EUR). Using dollars, it works:
DecimalFormat df = new DecimalFormat("¤#,##0.00", new DecimalFormatSymbols(Locale.US));
df.setCurrency(Currency.getInstance("EUR"));
BigDecimal value = new BigDecimal("1.23");
String text = df.format(value);
System.out.println(text);
DecimalFormat df2 = new DecimalFormat("¤#,##0.00", new DecimalFormatSymbols(Locale.US));
df2.setParseBigDecimal(true);
BigDecimal parsed = (BigDecimal) df2.parse(text);
BigDecimalAsserts.assertBigDecimalEquals("parsed value is the same of the original", value, parsed);

java (beginner) converting scientific notation to decimal

if
double d = 1.999e-4
I want my output to be 0.0001999.
How can I do it?
NumberFormat formatter = new DecimalFormat("###.#####");
String f = formatter.format(d);
You can explore the sub classes of NumberFormat class to know more details.
I suppose there is a method in BigDecimal Class called toPlainString().
e.g. if the the BigDecimal is 1.23e-8 then the method returns 0.0000000124.
BigDecimal d = new BigDecimal("1.23E-8");
System.out.println(d.toPlainString());
Above code prints 0.0000000123, then you can process the string as per your requirement.
You can do it like this:
double d = 1.999e-4;
NumberFormat nf = NumberFormat.getInstance();
nf.setMinimumFractionDigits(7);
System.out.println(nf.format(d));
Check out the documentation of NumberFormat's methods to format your double as you see fit.
DecimalFormat is a special case of NumberFormat as its constructor states, I don't think that you need its functionality for your case. Check out their documentation if you are confused. Use the factory method getInstance() of NumberFormat for your convenience.
If all you want is to print like that.
System.out.printf("%1$.10f", d);
you can change 10f, 10=number of decimal places you want.
Take a look over
java.text.DecimalFormat
and
java.text.DecimalFormatSymbols

How to change the decimal separator of DecimalFormat from comma to dot/point?

I have this little crazy method that converts BigDecimal values into nice and readable Strings.
private String formatBigDecimal(BigDecimal bd){
DecimalFormat df = new DecimalFormat();
df.setMinimumFractionDigits(3);
df.setMaximumFractionDigits(3);
df.setMinimumIntegerDigits(1);
df.setMaximumIntegerDigits(3);
df.setGroupingSize(20);
return df.format(bd);
}
It however, also produces a so called grouping separator "," that makes all my values come out like this:
xxx,xxx
I do need the separator to be a dot or a point and not a comma.
Does anybody have a clue of how to accomplish this little feat?
I have read this and in particular this to death now but I cannot find a way to get this done.
Am I approaching this the wrong way? Is there a much more elegant way of doing this? Maybe even a solution that accounts for different local number representations, since the comma would be perfect by European standards.
You can change the separator either by setting a locale or using the DecimalFormatSymbols.
If you want the grouping separator to be a point, you can use an european locale:
NumberFormat nf = NumberFormat.getNumberInstance(Locale.GERMAN);
DecimalFormat df = (DecimalFormat)nf;
Alternatively you can use the DecimalFormatSymbols class to change the symbols that appear in the formatted numbers produced by the format method. These symbols include the decimal separator, the grouping separator, the minus sign, and the percent sign, among others:
DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols(currentLocale);
otherSymbols.setDecimalSeparator(',');
otherSymbols.setGroupingSeparator('.');
DecimalFormat df = new DecimalFormat(formatString, otherSymbols);
currentLocale can be obtained from Locale.getDefault() i.e.:
Locale currentLocale = Locale.getDefault();
Europe is quite huge. I'm not sure if they use the same format all over. However this or this answer will be of help.
String text = "1,234567";
NumberFormat nf_in = NumberFormat.getNumberInstance(Locale.GERMANY);
double val = nf_in.parse(text).doubleValue();
NumberFormat nf_out = NumberFormat.getNumberInstance(Locale.UK);
nf_out.setMaximumFractionDigits(3);
String output = nf_out.format(val);
I.e. use the correct locale.
public String getGermanCurrencyFormat(double value) {
NumberFormat nf = NumberFormat.getNumberInstance(Locale.GERMAN);
nf.setGroupingUsed(true);
return "€ " + nf.format(value);
}
This worked in my case:
DecimalFormat df2 = new DecimalFormat("#.##");
df2.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ENGLISH));
BigDecimal does not seem to respect Locale settings.
Locale.getDefault(); //returns sl_SI
Slovenian locale should have a decimal comma. Guess I had strange misconceptions regarding numbers.
a = new BigDecimal("1,2") //throws exception
a = new BigDecimal("1.2") //is ok
a.toPlainString() // returns "1.2" always
I have edited a part of my message that made no sense since it proved to be due the human error (forgot to commit data and was looking at the wrong thing).
Same as BigDecimal can be said for any Java .toString() functions. I guess that is good in some ways. Serialization for example or debugging. There is an unique string representation.
Also as others mentioned using formatters works OK. Just use formatters, same for the JSF frontend, formatters do the job properly and are aware of the locale.
String money = output.replace(',', '.');
you could just use replace function before you return the string in the method
return df.format(bd).replace(",", ".")
This worked for me...
double num = 10025000;
new DecimalFormat("#,###.##");
DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance(Locale.GERMAN);
System.out.println(df.format(num));

Categories