web-dev-qa-db-fra.com

Java - Format décimal.parse pour renvoyer une double valeur avec le nombre de décimales spécifié

Je veux être capable de convertir une chaîne en Double étant donné un nombre de décimales dans une chaîne de format. Donc "###, ## 0.000" devrait me donner un Double à 3 décimales.

Edit - ajout de plus d'informations à ce qui se passe

L'utilisateur entre la valeur dans l'interface utilisateur - qui est entrée dans une chaîne. La règle est que cette valeur est limitée à 3 décimales. Le code sous-jacent stocke la valeur dans la base de données qui est ensuite utilisée dans un calcul. Par conséquent, les décimales suivantes feront que les calculs seront légèrement à la hauteur des attentes.

J'ai le code suivant:

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

Le DecimalFormat semble ignorer la chaîne de format et me donne la chaîne complète en tant que Double de 1.234567.

DecimalFormat peut-il être obligé d'utiliser la chaîne de format lors de l'analyse? Est-ce que je manque quelque chose?

À votre santé,

Andez

12
Andez

Prenant en compte ce que vous avez dit, j'ai légèrement modifié mon code pour couvrir différents endroits. La clé prenait une chaîne de valeur dans un format localisé en un fichier Double arrondi en fonction de la chaîne de format.

La chaîne de formatage est toujours un format basé au Royaume-Uni avec les séparateurs décimaux spécifiés comme "." et mille séparateurs spécifiés comme ",".

J'utilise DecimalFormat pour analyser initialement le format localisé en fonction des paramètres régionaux spécifiés. Cela donne un double équivalent de la chaîne correctement. J'utilise ensuite un BigDecimal pour gérer l'arrondi. Je peux obtenir le nombre de décimales à partir de l'instance DecimalFormat et appeler setScale sur BigDecimal pour effectuer l'arrondi.

La structure de code initiale a été modifiée pour vous permettre de voir ce qui se passe dans différentes circonstances de paramètres régionaux, grâce à @ RD01 pour avoir noté l’importance des autres paramètres régionaux. 

J'ai maintenant le code comme suit:

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

Cela produit la sortie suivante:

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 est utilisé à deux fins distinct : analyser une entrée et formater une sortie. Si vous voulez faire les deux, vous devrez utiliser l'objet de format deux fois.

Si vous souhaitez prendre cette valeur et formater la sortie en limitant le nombre de chiffres significatifs, vous devez utiliser à nouveau l'objet de formatage. Cette fois, il utilise vos règles de formatage pour créer une chaîne de sortie à partir d'une valeur numérique:

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

Cela vous donnera la sortie de 1,235

Il semble que vous souhaitiez que cette valeur numérique soit présentée au format 1.235. Pour ce faire, vous devez formater la sortie en utilisant des paramètres régionaux spécifiques (si le vôtre utilise un format différent).

CEPENDANT, je recommanderais d'aborder ce problème différemment:

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

Quelques notes: 

  • L'analyse des entrées doit être séparée du formatage de la sortie. Surtout quand vous commencez à jeter dans plusieurs Locales. 
  • Autorisez la bibliothèque standard à effectuer le gros travail pour déterminer quelle valeur d'entrée valide correspond à un paramètre régional donné. Il vous suffit de sélectionner un paramètre régional approprié (j'ai choisi ALLEMAGNE, mais cela fonctionnerait évidemment avec d'autres). Toujours utiliser les paramètres régionaux lorsque cela est possible. N'essayez pas de recréer des chaînes de formatage.
  • Stockez toujours votre valeur d’entrée SÉPARÉE de tout formatage de sortie. Par exemple, si vous voulez afficher seulement trois chiffres dans la sortie, c'est très bien, mais stockez quand même la valeur double entière.
18
robert_x44

La restriction de décimales dans DecimalFormat est vraiment destinée à être utilisée dans la méthode format() et n'a pas beaucoup d'effet dans la méthode parse().

Pour obtenir ce que vous voulez, vous avez besoin de ceci:

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

Vous devez d’abord obtenir le numéro sous forme de chaîne de format UK, puis l’analyser à l’aide du programme de formatage UK. Cela vous donnera le résultat que vous recherchez. NB, cela arrondira le nombre à 3 décimales, pas tronquer.

En passant, je suis un peu surpris que votre formateur britannique puisse analyser le numéro de format CE. Vous devriez vraiment analyser le numéro d'origine avec un analyseur au format CE.

1
mluisbrown

Sûr que vous pouvez. Essayez de lancer ceci:

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

Clairement, ils produisent une sortie différente, la première lecture 1.234567 et la seconde 1234567. Peut-être qu'il y a quelque chose qui ne va pas avec votre modèle? Quoi qu’il en soit, la dernière ligne serait le moyen privilégié d’obtenir le format standard UK.

0
Mark Peters