java - partie - veuillez entrer le coût total des dépenses avec une virgule décimale soit 100 00




Comment arrondir un nombre à n décimales en Java (20)

Ce que je voudrais est une méthode pour convertir un double en une chaîne qui arrondit en utilisant la méthode demi-up - c'est-à-dire si la décimale à arrondir est 5, elle arrondit toujours au nombre précédent. C'est la méthode standard d'arrondi que la plupart des gens attendent dans la plupart des situations.

Je voudrais également que seuls les chiffres significatifs soient affichés - c'est-à-dire qu'il ne devrait pas y avoir de zéros de fin.

Je sais qu'une méthode consiste à utiliser la méthode String.format :

String.format("%.5g%n", 0.912385);

résultats:

0.91239

ce qui est génial, mais il affiche toujours des nombres avec 5 décimales même s'ils ne sont pas significatifs:

String.format("%.5g%n", 0.912300);

résultats:

0.91230

Une autre méthode consiste à utiliser le DecimalFormatter :

DecimalFormat df = new DecimalFormat("#.#####");
df.format(0.912385);

résultats:

0.91238

Cependant, comme vous pouvez le voir, cela utilise des arrondis à moitié égales. C'est-à-dire qu'il arrondira à la baisse si le chiffre précédent est pair. Ce que j'aimerais, c'est ceci:

0.912385 -> 0.91239
0.912300 -> 0.9123

Quel est le meilleur moyen d'y parvenir en Java?


@Milhous: le format décimal pour l'arrondi est excellent:

Vous pouvez également utiliser le

DecimalFormat df = new DecimalFormat("#.00000");
df.format(0.912385);

pour vous assurer que vous avez les 0 à la fin.

J'ajouterais que cette méthode est très efficace pour fournir un mécanisme d'arrondi numérique réel - non seulement visuellement, mais aussi lors du traitement.

Hypothétique: vous devez implémenter un mécanisme d'arrondi dans un programme GUI. Pour modifier l'exactitude / la précision d'une sortie de résultat, il suffit de changer le format du caret (c'est-à-dire entre les parenthèses). Pour que:

DecimalFormat df = new DecimalFormat("#0.######");
df.format(0.912385);

reviendrait en sortie: 0.912385

DecimalFormat df = new DecimalFormat("#0.#####");
df.format(0.912385);

reviendrait en sortie: 0.91239

DecimalFormat df = new DecimalFormat("#0.####");
df.format(0.912385);

reviendrait en sortie: 0.9124

[EDIT: aussi si le format du caret est comme ça ("# 0. ############") et que vous entrez un nombre décimal, par exemple 3.1415926, pour l'argument, DecimalFormat ne produit pas d'ordures ( par exemple zéros à la traîne) et reviendra: 3.1415926 .. si vous êtes de cette façon incliné. Certes, c'est un peu bavard pour le goût de certains développeurs - mais bon, il a une faible empreinte mémoire lors du traitement et est très facile à mettre en œuvre.]

Donc, essentiellement, la beauté de DecimalFormat est qu'il gère simultanément l'apparence de la chaîne - ainsi que le niveau de précision de l'arrondi. Ergo: vous obtenez deux avantages pour le prix d'une implémentation de code. ;)


Comme d'autres l'ont noté, la bonne réponse consiste à utiliser DecimalFormat ou BigDecimal . Le virgule flottante n'a pas de décimales, donc vous ne pouvez pas arrondir / tronquer à un nombre spécifique d'entre eux en premier lieu. Vous devez travailler dans une base décimale, et c'est ce que font ces deux classes.

Je poste le code suivant comme un contre-exemple pour toutes les réponses dans ce fil de discussion et en fait partout dans (et ailleurs) qui recommandent une multiplication suivie d'une troncature suivie d'une division. Il incombe aux partisans de cette technique d'expliquer pourquoi le code suivant produit le mauvais résultat dans plus de 92% des cas.

public class RoundingCounterExample
{

    static float roundOff(float x, int position)
    {
        float a = x;
        double temp = Math.pow(10.0, position);
        a *= temp;
        a = Math.round(a);
        return (a / (float)temp);
    }

    public static void main(String[] args)
    {
        float a = roundOff(0.0009434f,3);
        System.out.println("a="+a+" (a % .001)="+(a % 0.001));
        int count = 0, errors = 0;
        for (double x = 0.0; x < 1; x += 0.0001)
        {
            count++;
            double d = x;
            int scale = 2;
            double factor = Math.pow(10, scale);
            d = Math.round(d * factor) / factor;
            if ((d % 0.01) != 0.0)
            {
                System.out.println(d + " " + (d % 0.01));
                errors++;
            }
        }
        System.out.println(count + " trials " + errors + " errors");
    }
}

Sortie de ce programme:

10001 trials 9251 errors

EDIT: Pour répondre à certains commentaires ci-dessous, je redis la partie module de la boucle de test en utilisant BigDecimal et new MathContext(16) pour l'opération de module comme suit:

public static void main(String[] args)
{
    int count = 0, errors = 0;
    int scale = 2;
    double factor = Math.pow(10, scale);
    MathContext mc = new MathContext(16, RoundingMode.DOWN);
    for (double x = 0.0; x < 1; x += 0.0001)
    {
        count++;
        double d = x;
        d = Math.round(d * factor) / factor;
        BigDecimal bd = new BigDecimal(d, mc);
        bd = bd.remainder(new BigDecimal("0.01"), mc);
        if (bd.multiply(BigDecimal.valueOf(100)).remainder(BigDecimal.ONE, mc).compareTo(BigDecimal.ZERO) != 0)
        {
            System.out.println(d + " " + bd);
            errors++;
        }
    }
    System.out.println(count + " trials " + errors + " errors");
}

Résultat:

10001 trials 4401 errors

DecimalFormat est le meilleur moyen de sortie, mais je ne le préfère pas. Je le fais toujours tout le temps, parce qu'il renvoie la double valeur. Je peux donc l'utiliser plus que juste la sortie.

Math.round(selfEvaluate*100000d.0)/100000d.0;

OU

Math.round(selfEvaluate*100000d.0)*0.00000d1;

Si vous avez besoin d'une grande valeur décimale, vous pouvez utiliser BigDecimal à la place. Quoi qu'il en soit .0 est important. Sans cela, l'arrondi de 0.33333d5 renvoie 0.33333 et seuls 9 chiffres sont autorisés. La deuxième fonction sans .0 a des problèmes avec 0.30000 renvoie 0.30000000000000004.


En supposant que la value est un double , vous pouvez faire:

(double)Math.round(value * 100000d) / 100000d

C'est pour la précision de 5 chiffres. Le nombre de zéros indique le nombre de décimales.


Gardez à l'esprit que String.format () et DecimalFormat produisent une chaîne utilisant Locale par défaut. Ils peuvent donc écrire un nombre formaté avec un point ou une virgule comme séparateur entre les parties entières et décimales. Pour vous assurer que la chaîne arrondie est au format que vous voulez, utilisez java.text.NumberFormat comme suit:

  Locale locale = Locale.ENGLISH;
  NumberFormat nf = NumberFormat.getNumberInstance(locale);
  // for trailing zeros:
  nf.setMinimumFractionDigits(2);
  // round to 2 digits:
  nf.setMaximumFractionDigits(2);

  System.out.println(nf.format(.99));
  System.out.println(nf.format(123.567));
  System.out.println(nf.format(123.0));

Will print in English locale (no matter what your locale is): 0.99 123.57 123.00

The example is taken from Farenda - how to convert double to String correctly .


Je suis d'accord avec la réponse choisie pour utiliser DecimalFormat --- ou BigDecimal .

Veuillez lire la mise à jour ci-dessous en premier!

Cependant, si vous voulez arrondir la double valeur et obtenir un double résultat, vous pouvez utiliser org.apache.commons.math3.util.Precision.round(..) comme indiqué ci-dessus. L'implémentation utilise BigDecimal , est lente et crée des erreurs.

Une méthode similaire mais rapide et sans déchet est fournie par l'utilitaire DoubleRounder dans la bibliothèque decimal4j:

 double a = DoubleRounder.round(2.0/3.0, 3);
 double b = DoubleRounder.round(2.0/3.0, 3, RoundingMode.DOWN);
 double c = DoubleRounder.round(1000.0d, 17);
 double d = DoubleRounder.round(90080070060.1d, 9);
 System.out.println(a);
 System.out.println(b);
 System.out.println(c);
 System.out.println(d);

Sortira

 0.667
 0.666
 1000.0
 9.00800700601E10

Voir https://github.com/tools4j/decimal4j/wiki/DoubleRounder-Utility

Disclaimer: Je suis impliqué dans le projet decimal4j.

Mise à jour: Comme l'a souligné @iaforek, DoubleRounder renvoie parfois des résultats contre-intuitifs. La raison en est qu'il effectue des arrondis mathématiquement corrects. Par exemple, DoubleRounder.round(256.025d, 2) sera arrondi à 256.02 car la valeur double représentée par 256.025d est légèrement inférieure à la valeur rationnelle 256.025 et sera donc arrondie à la valeur inférieure.

Remarques:

  • Ce comportement est très similaire à celui du constructeur BigDecimal(double) (mais pas à valueOf(double) qui utilise le constructeur de chaîne).
  • Le problème peut être contourné avec une double étape d'arrondi à une plus grande précision d'abord, mais c'est compliqué et je ne vais pas dans les détails ici

Pour ces raisons et tout ce qui est mentionné ci-dessus dans ce post, je ne peux pas recommander d'utiliser DoubleRounder .


Juste au cas où quelqu'un aurait encore besoin d'aide pour ça. Cette solution fonctionne parfaitement pour moi.

private String withNoTrailingZeros(final double value, final int nrOfDecimals) {
return new BigDecimal(String.valueOf(value)).setScale(nrOfDecimals,  BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString();

}

renvoie une String avec la sortie souhaitée.


L'extrait de code ci-dessous montre comment afficher n chiffres. L'astuce consiste à mettre la variable pp à 1 suivie de n zéros. Dans l'exemple ci-dessous, la valeur variable pp a 5 zéros, donc 5 chiffres seront affichés.

double pp = 10000;

double myVal = 22.268699999999967;
String needVal = "22.2687";

double i = (5.0/pp);

String format = "%10.4f";
String getVal = String.format(format,(Math.round((myVal +i)*pp)/pp)-i).trim();

Real's Java How-to posts cette solution, qui est également compatible avec les versions antérieures à Java 1.6.

BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
return bd.doubleValue();

Si vous utilisez DecimalFormat pour convertir le double en String , c'est très simple:

DecimalFormat formatter = new DecimalFormat("0.0##");
formatter.setRoundingMode(RoundingMode.HALF_UP);

double num = 1.234567;
return formatter.format(num);

Vous pouvez sélectionner plusieurs valeurs d'énumération RoundingMode , en fonction du comportement requis.


Supposons que vous avez

double d = 9232.129394d;

vous pouvez utiliser BigDecimal

BigDecimal bd = new BigDecimal(d).setScale(2, RoundingMode.HALF_EVEN);
d = bd.doubleValue();

ou sans BigDecimal

d = Math.round(d*100)/100.0d;

avec les deux solutions d == 9232.13


Utilisez setRoundingMode , définissez explicitement le RoundingMode pour gérer votre problème avec le demi-rond, puis utilisez le modèle de format pour votre sortie requise.

Exemple:

DecimalFormat df = new DecimalFormat("#.####");
df.setRoundingMode(RoundingMode.CEILING);
for (Number n : Arrays.asList(12, 123.12345, 0.23, 0.1, 2341234.212431324)) {
    Double d = n.doubleValue();
    System.out.println(df.format(d));
}

donne la sortie:

12
123.1235
0.23
0.1
2341234.2125

Voici une solution de base offerte par jpdymond :

   public static double round(double value, int precision) {
      int scale = (int) Math.pow(10, precision);
      return (double) Math.round(value * scale) / scale;
  }

https://.com/a/22186845/212950


Vous pouvez également utiliser le

DecimalFormat df = new DecimalFormat("#.00000");
df.format(0.912385);

pour vous assurer que vous avez les 0 à la fin.


Vous pouvez utiliser la classe DecimalFormat.

double d = 3.76628729;

DecimalFormat newFormat = new DecimalFormat("#.##");
double twoDecimal =  Double.valueOf(newFormat.format(d));

Vous pouvez utiliser la méthode d'utilité suivante

public static double round(double valueToRound, int numberOfDecimalPlaces)
{
    double multipicationFactor = Math.pow(10, numberOfDecimalPlaces);
    double interestedInZeroDPs = valueToRound * multipicationFactor;
    return Math.round(interestedInZeroDPs) / multipicationFactor;
}

If you're using a technology that has a minimal JDK. Here's a way without any Java libs:

double scale = 100000;    
double myVal = 0.912385;
double rounded = (int)((myVal * scale) + 0.5d) / scale;

In general, rounding is done by scaling: round(num / p) * p

/**
 * MidpointRounding away from zero ('arithmetic' rounding)
 * Uses a half-epsilon for correction. (This offsets IEEE-754
 * half-to-even rounding that was applied at the edge cases).
 */
double RoundCorrect(double num, int precision) {
	double c = 0.5 * EPSILON * num;
//	double p = Math.pow(10, precision); //slow
	double p = 1; while (precision--> 0) p *= 10;
	if (num < 0)
		p *= -1;
	return Math.round((num + c) * p) / p;
}

// testing edge cases
RoundCorrect(1.005, 2);   // 1.01 correct
RoundCorrect(2.175, 2);   // 2.18 correct
RoundCorrect(5.015, 2);   // 5.02 correct

RoundCorrect(-1.005, 2);  // -1.01 correct
RoundCorrect(-2.175, 2);  // -2.18 correct
RoundCorrect(-5.015, 2);  // -5.02 correct


DecimalFormat myFormatter = new DecimalFormat("0.000");
String output = myFormatter.format(2.34d);

double myNum = .912385;
int precision = 10000; //keep 4 digits
myNum= Math.floor(myNum * precision +.5)/precision;




digits