Java - Decimal Format.parse для возврата двойного значения с указанным количеством десятичных знаков - PullRequest
12 голосов
/ 19 января 2011

Я хочу иметь возможность преобразовать строку в Double , учитывая количество знаков после запятой в строке формата.Так что "###, ## 0.000" должно дать мне Double до 3 десятичных знаков.

Edit - добавлено больше информации о том, что происходит

Пользователь вводитзначение в пользовательском интерфейсе - который вводится в строку.Правило - это значение ограничено 3 десятичными знаками.Базовый код хранит значение в базе данных, которое затем используется в расчете.Поэтому конечные десятичные разряды приведут к тому, что вычисления будут немного меньше ожидаемого.

У меня есть следующий код:

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

        // 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");
    }
}

Кажется, что DecimalFormat игнорирует строку формата и даетполную строку в виде Double из 1.234567.

Может ли DecimalFormat принудительно использовать строку формата при разборе?Я что-то упустил?

Ура,

Андез

Ответы [ 4 ]

18 голосов
/ 19 января 2011

DecimalFormat используется для двух различных целей: анализа ввода и форматирования вывода. Если вы хотите сделать и то и другое, вам придется использовать объект формата дважды.

Если вы хотите взять это значение и отформатировать вывод, ограничивая количество значащих цифр, вам необходимо снова использовать объект форматирования. На этот раз он использует ваши правила форматирования для создания выходной строки из числового значения:

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

Это даст вам вывод 1,235

Кажется, вы хотите, чтобы это числовое значение было представлено в формате 1.235. Для этого вы должны отформатировать вывод с использованием определенной локали (если ваша использует другой формат).

ОДНАКО , я бы рекомендовал подходить к этой проблеме по-другому:

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

Несколько заметок:

  • Разбор ввода должен храниться отдельно от форматирования вывода. Особенно, когда вы начинаете бросать в нескольких локалях.
  • Разрешить стандартной библиотеке выполнять тяжелую работу для определения допустимого значения ввода для данной локали. Вам просто нужно выбрать соответствующую локаль (я выбрал ГЕРМАНИЮ, но это, очевидно, будет работать с другими) Всегда используйте Locales, когда это возможно. Не пытайтесь воссоздать строки форматирования.
  • Всегда сохраняйте входное значение SEPARATE для любого выходного форматирования. То есть, если вы хотите показать только три цифры в выводе, это нормально, но в любом случае сохраните все двойное значение.
4 голосов
/ 20 января 2011

Принимая во внимание то, что вы сказали, я немного изменил свой код, чтобы охватить разные локали. Ключ переводил строку значения в локализованном формате в Double, округленный на основе строки формата.

Строка формата всегда является британским форматом с десятичными разделителями, указанными как "." и тысячи разделителей, указанных как ",".

Я использую DecimalFormat для первоначального анализа локализованного формата на основе указанной локали. Это дает двойной эквивалент строки правильно. Затем я использую BigDecimal для обработки округления. Я могу получить количество десятичных разрядов из экземпляра DecimalFormat и вызвать setScale в BigDecimal для выполнения округления.

Исходная структура кода была изменена, чтобы вы могли видеть, что происходит при различных обстоятельствах в локали, спасибо @ RD01 за внимание к важности других локалей.

Теперь у меня есть код:

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

Создает следующий вывод:

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
1 голос
/ 19 января 2011

Ограничение десятичных знаков в DecimalFormat действительно предназначено для использования в методе format() и не имеет большого эффекта в методе parse().

Чтобы получить то, что вы хотите, вам нужно:

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

Сначала вам нужно получить номер в виде строки формата Великобритании, а затем проанализировать этот номер, используя средство форматирования Великобритании. Это даст вам результат, который вы ищете. Обратите внимание, что это округляет число до 3 десятичных знаков, а не усекает.

Кстати, я немного удивлен, что ваш британский форматировщик может проанализировать номер формата CE. Вы действительно должны анализировать исходное число с помощью синтаксического анализатора формата CE.

0 голосов
/ 19 января 2011

Конечно, вы можете. Попробуйте запустить это:

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

Очевидно, что они приводят к разным выводам: первое чтение 1.234567 и второе 1234567. Может быть, что-то не так с вашим рисунком? В любом случае, последняя строка будет предпочтительным способом получения стандартного формата UK.

...