Java - Decimal Format.parse to return double value with specified number of decimal places

人盡茶涼 提交于 2019-11-28 09:57:13

DecimalFormat is used for two distinct purposes: parsing input and formatting output. If you want to do both, you'll have to use the format object twice.

If you want to take that value and format the output, restricting the number of significant digits, you need to use the format object again. This time it uses your formatting rules to create an output string from a numeric value:

String output = formatterUK.format(valCEWithUKFormat.doubleValue() );

This will give you the output of 1,235

It seems you want this numeric value to be presented in the 1.235 format. To do this, you should format the output using a specific locale (if yours uses a different format).

HOWEVER, I would recommend approaching this problem differently:

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

A few notes:

  • Input parsing should be kept separate from output formatting. Especially when you start throwing in multiple Locales.
  • Allow the standard library to do the heavy lifting for determining what a valid input value is for a given Locale. You just need to select an appropriate Locale (I chose GERMANY, but this would obviously work with others). Always use Locales when possible. Don't try to recreate formatting strings.
  • Always store your input value SEPARATE from any output formatting. IE, if you want to show only three digits in the output, that's fine, but store the whole double value anyway.

Taking on board what you said I have modified my code slightly to cover different locales. The key was taking a value string in a localised format to a Double that is rounded based on the format string.

The format string is always a UK based format with the decimal seperators specified as "." and thousand seperators specified as ",".

I am using the DecimalFormat to initially parse the localised format based on a specified locale. This gives a Double equivalent of the string correctly. I then use a BigDecimal to handle the rounding. I can get the number of decimal places from the DecimalFormat instance and call setScale on the BigDecimal to perform the rounding.

The initial code structure has been modified to allow you to see what happens under different locale circumstances thanks @RD01 for noting importance of other locales.

I now have code as follows:

private void runTests3() {
    // output current locale we are running under
    System.out.println( "Current Locale is " + Locale.getDefault().toString() );

    // number in Central European Format with a format string specified in UK format
    String numbersInEuropeanFormatString[] = new String[] { "1.000,234567", "1,2345678", "1.222.333,234567" };
    String formatUK = "###,##0.0000";

    // output numbers using the german locale
    System.out.println("Output numbers using the German locale\n");
    for(String num : numbersInEuropeanFormatString ) {
        formatNumberAsDouble(num, formatUK, Locale.GERMAN);
    }

    // output numbers using the UK locale.  
    // this should return unexpected results as the number is in European format
    System.out.println("Output numbers using the UK locale\n");
    for(String num : numbersInEuropeanFormatString ) {
        formatNumberAsDouble(num, formatUK, Locale.UK);
    }

    // output numbers using new DecimalFormat( formatUK ) - no locale specified
    System.out.println("\n\nOutput numbers using new DecimalFormat( " + formatUK + " )\n");
    for(String num : numbersInEuropeanFormatString ) {
        formatNumberAsDouble( num, formatUK, null);
    }
}

private void formatNumberAsDouble(String value, String format, Locale locale) {


    NumberFormat formatter;
    int decimalPlaces;

    // create the formatter based on the specified locale
    if( locale != null ) {
         formatter = NumberFormat.getNumberInstance(locale);
         // creating the above number format does not take in the format string
         // so create a new one that we won't use at all just to get the
         // decimal places in it
         decimalPlaces = (new DecimalFormat(format)).getMaximumFractionDigits();
    } else {
        formatter = new DecimalFormat( format );
        decimalPlaces = formatter.getMaximumFractionDigits();
    }

    // get the result as number
    Double result = null;
    try {
        result = formatter.parse( value ).doubleValue();
    } catch( ParseException ex ) {
        // not bothered at minute
    }

    // round the Double to the precision specified in the format string


    BigDecimal bd = new BigDecimal(result );
    Double roundedValue = bd.setScale( decimalPlaces, RoundingMode.HALF_UP ).doubleValue();

    // output summary
    System.out.println("\tValue = " + value);
    System.out.println( locale == null  ? "\tLocale not specified" : "\tLocale = " + locale.toString());
    System.out.println( format == null || format.length() == 0 ? "\tFormat = Not specified" : "\tFormat = " + format);
    System.out.println("\tResult (Double) = " + result);
    System.out.println("\tRounded Result (Double) (" + decimalPlaces + "dp) = " + roundedValue);
    System.out.println("");
}

This produces the following output:

Current Locale is nl_BE
Output numbers using the German locale

    Value = 1.000,234567
    Locale = de
    Format = ###,##0.0000
    Result (Double) = 1000.234567
    Rounded Result (Double) (4dp) = 1000.2346

    Value = 1,2345678
    Locale = de
    Format = ###,##0.0000
    Result (Double) = 1.2345678
    Rounded Result (Double) (4dp) = 1.2346

    Value = 1.222.333,234567
    Locale = de
    Format = ###,##0.0000
    Result (Double) = 1222333.234567
    Rounded Result (Double) (4dp) = 1222333.2346

Output numbers using the UK locale

    Value = 1.000,234567
    Locale = en_GB
    Format = ###,##0.0000
    Result (Double) = 1.0
    Rounded Result (Double) (4dp) = 1.0

    Value = 1,2345678
    Locale = en_GB
    Format = ###,##0.0000
    Result (Double) = 1.2345678E7
    Rounded Result (Double) (4dp) = 1.2345678E7

    Value = 1.222.333,234567
    Locale = en_GB
    Format = ###,##0.0000
    Result (Double) = 1.222
    Rounded Result (Double) (4dp) = 1.222



Output numbers using new DecimalFormat( ###,##0.0000 )

    Value = 1.000,234567
    Locale not specified
    Format = ###,##0.0000
    Result (Double) = 1000.234567
    Rounded Result (Double) (4dp) = 1000.2346

    Value = 1,2345678
    Locale not specified
    Format = ###,##0.0000
    Result (Double) = 1.2345678
    Rounded Result (Double) (4dp) = 1.2346

    Value = 1.222.333,234567
    Locale not specified
    Format = ###,##0.0000
    Result (Double) = 1222333.234567
    Rounded Result (Double) (4dp) = 1222333.2346

The restriction of decimal places in DecimalFormat is really meant for use in the format() method and doesn't have much effect in the parse() method.

In order to get what you want you need this:

    try {
        // output current locale we are running under (this happens to be "nl_BE")
        System.out.println("Current Locale is " + Locale.getDefault().toString());

        // number in Central European Format with a format string specified in UK format
        String numberCE = "1,234567"; // 1.234567
        String formatUK = "###,##0.000";

        // do the format
        DecimalFormat formatterUK = new DecimalFormat(formatUK);
        Double valCEWithUKFormat = formatterUK.parse(numberCE).doubleValue();

        // first convert to UK format string
        String numberUK = formatterUK.format(valCEWithUKFormat);
        // now parse that string to a double
        valCEWithUKFormat = formatterUK.parse(numberUK).doubleValue();

        // I want the number to DPs in the format string!!!
        System.out.println("CE Value     " + numberCE + " in UK format (" + formatUK + ") is " + valCEWithUKFormat);

    } catch (ParseException ex) {
        System.out.println("Cannot parse number");
    }

You first need to get the number as a UK format string and then parse that number, using the UK formatter. That will get you the result you're looking for. NB, this will round the number to 3 decimal places, not truncate.

By the way, I'm slightly surprised that your UK formatter is able to parse the CE format number. You really should be parsing the original number with a CE format parser.

Sure you can. Try running this:

String in = "1,234567";
System.out.println(NumberFormat.getNumberFormat(new Locale("fr", "FR")).parse(in));
System.out.println(NumberFormat.getNumberFormat(new Locale("en", "GB")).parse(in));

Clearly they result in different output, the first reading 1.234567 and the second 1234567. Maybe there's something wrong with your pattern? Anyway the last line there would be the preferred way of getting the UK standard format.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!