web-dev-qa-db-ja.com

Java-Decimal Format.parseで指定された小数点以下の桁数のdouble値を返す

フォーマット文字列の小数点以下の桁数を指定して、文字列をDoubleに変換できるようにしたい。したがって、 "###、## 0.000"は小数点以下3桁までDoubleを与えます。

編集-何が起こるかについての追加情報

ユーザーは、UIに値を入力します。これは文字列に入力されます。原則として、この値は小数点以下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 of 1.234567として与えます。

DecimalFormatは解析時にフォーマット文字列を使用するように強制できますか?何か不足していますか?

乾杯、

アンデス

12
Andez

あなたの言ったことを踏まえて、さまざまなロケールをカバーするようにコードを少し変更しました。キーは、ローカライズされた形式の値文字列を、形式文字列に基づいて丸められたDoubleに変換していました。

書式文字列は、常に英国ベースの形式で、小数点記号は "。"として指定されます。 "、"として指定された1000の区切り記号。

DecimalFormatを使用して、指定されたロケールに基づいてローカライズされた形式を最初に解析しています。これにより、文字列と同等のDoubleが正しく得られます。次に、丸めを処理するためにBigDecimalを使用します。 DecimalFormatインスタンスから小数点以下の桁数を取得し、BigDecimalでsetScaleを呼び出して丸めを実行できます。

他のロケールの重要性を指摘してくれた@ 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
4
Andez

DecimalFormatは、2つのdistinct目的で使用されます:入力の解析と出力のフォーマット。両方を行う場合は、フォーマットオブジェクトを2回使用する必要があります。

その値を取得して出力をフォーマットし、有効桁数を制限する場合は、フォーマットオブジェクトを再度使用する必要があります。今回は、フォーマット規則を使用して、数値から出力文字列を作成します。

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

いくつかのメモ:

  • 入力の解析は、出力のフォーマットとは別に行う必要があります。特に、複数のロケールで投げ始めたとき。
  • 標準ライブラリが、特定のロケールに対して有効な入力値が何であるかを決定するための重労働を実行できるようにします。適切なロケールを選択するだけで済みます(私はドイツを選択しましたが、これは明らかに他の言語でも機能します)。可能な場合は常にロケールを使用してください。フォーマット文字列を再作成しないでください。
  • すべての出力フォーマットから常に入力値SEPARATEを保存します。 IE、出力に3桁のみを表示したい場合は問題ありませんが、とにかくdouble値全体を格納します。
18
robert_x44

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

最初に数値をUKフォーマット文字列として取得し、次にUKフォーマッタを使用してその数値を解析する必要があります。それはあなたが探している結果を得るでしょう。注:これにより、数値は小数点以下3桁に丸められ、切り捨てられません。

ちなみに、英国のフォーマッターがCE形式の番号を解析できることに少し驚いています。実際には、CE形式のパーサーで元の数値を解析する必要があります。

1
mluisbrown

できますよ。これを実行してみてください:

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と2番目の1234567。たぶんあなたのパターンに何か問題がありますか?とにかく最後の行は、UK標準形式を取得するための推奨される方法です。

0
Mark Peters