madkimchi
madkimchi

Reputation: 77

How to format a number based on locale, while retaining all decimal points?

I'm trying to use DecimalFormat to convert the Decimal separator of a double value while retaining all decimals of the original number. DecimalFormatter accepts a pattern in the format: "0.##", for example. Since I have to use numbers with varying decimals, this will not work, however, since the number of decimals always needs to be specified in the pattern.

I am looking for a way to get around this.

I've tried String.format. DecimaFormatter and NumberFormatter

What I would like ideally is something along the lines of:

  private static final ThreadLocal< DecimalFormat > formatter = new ThreadLocal< DecimalFormat >() 
  {
    @Override
    protected DecimalFormat initialValue() 
    {
    // n should be any number of decimals without having to specify them.
      return new DecimalFormat("0.0#n");     
    }
  };

Some examples:

DecimalFormat df = new DecimalFormat("0.0##");
System.out.println(df.format(2.456))
System.out.println(df.format(2.1));

Result:

2,456 -> Good
2,100 -> Not good

I want to set a pattern/regex which will work for doubles of any number of digits after the decimal separator like:

2,456 -> Good
2,1 -> Good
3,3453456345234 -> Good

Upvotes: 3

Views: 2726

Answers (3)

Andreas
Andreas

Reputation: 159086

DecimalFormat has 16 setter methods for controlling the output. The pattern string you specify in the constructor is just a convenient way to set most of those values.

To control the locale of the output, you can specify the DecimalFormatSymbols, either in the constructor or in a setter method.

Since you want unlimited decimal places, and it seems you want at least one decimal place, you need to call a setter method, because specifying unlimited is not possible with the pattern string.

Example

private static void test(Locale locale) {
    DecimalFormat fmt = new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(locale));
    //fmt.setMaximumIntegerDigits(Integer.MAX_VALUE); // already set by pattern
    //fmt.setMinimumIntegerDigits(1);                 // already set by pattern
    //fmt.setMinimumFractionDigits(1);                // already set by pattern
    fmt.setMaximumFractionDigits(Integer.MAX_VALUE);
    //fmt.setGroupingUsed(false);                     // already set by pattern

    System.out.println(locale.toLanguageTag() + " (" + locale.getDisplayLanguage(Locale.US) +
                                               " - " + locale.getDisplayCountry(Locale.US) + ")");
    System.out.println("  " + fmt.format(123456789));
    System.out.println("  " + fmt.format(2.456));
    System.out.println("  " + fmt.format(2.1));
    System.out.println("  " + fmt.format(3.3453456345234));
}

public static void main(String[] args) {
    test(Locale.US);
    test(Locale.GERMANY);
    test(Locale.forLanguageTag("ar-EG"));
}

Output (OpenJDK 11.0.1)

en-US (English - United States)
  123456789.0
  2.456
  2.1
  3.3453456345234
de-DE (German - Germany)
  123456789,0
  2,456
  2,1
  3,3453456345234
ar-EG (Arabic - Egypt)
  ١٢٣٤٥٦٧٨٩٫٠
  ٢٫٤٥٦
  ٢٫١
  ٣٫٣٤٥٣٤٥٦٣٤٥٢٣٤

Upvotes: 0

Joop Eggen
Joop Eggen

Reputation: 109547

So you have

double[] floats = { 3.20, 4.500, 6.34, 1.0000 };
BigDecimal[] fixeds = {
    new BigDecimal("3.20"),
    new BigDecimal("4.500"),
    new BigDecimal("6.34"),
    new BigDecimal("1.0000")
};

and want to format them as such, but localized.

The good news is that fixed point numbers with BigDecimal maintain a precision (using the string constructor). The bad news is that floating point is alwaya an approximation, by a sum of (negative) powers of 2. So 3.20 might actually be 3.19999987 or 3.20000043.

They have no fixed decimals. Even without approximation error 3.2 == 3.20 == 3.200.

So convert to BigDecimal (quite ugly), and get rid of the approximation errors: 1000*3.2 != 3200.0.

BigDecimal value = new BigDecimal("5.1000");
NumberFormat df = NumberFormat.getInstance(Locale.KOREA);
df.setMinimumFractionDigits(value.getScale()); // 4
String s = df.format(value);

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074148

Numbers in Java (and just numbers, generally) don't have a set number of decimal places. 1.1, 1.10, and 1.100 are all exactly the same number.

You could find out how many places default formatting would use, e.g.:

String str = num.toString();
int decimal = str.indexOf('.');
int places = decimal <= 0 ? 0 : str.length - decimal;

...and then specify that many places when using the formatter.

Upvotes: 3

Related Questions