Conserver la précision avec double en Java

public class doublePrecision {
    public static void main(String[] args) {

        double total = 0;
        total += 5.6;
        total += 5.8;
        System.out.println(total);
    }
}

le code ci-dessus imprime:

11.399999999999

Comment puis-je obtenir que ce soit imprimé (ou que je puisse l'utiliser comme) 11.4?

118
demandé sur Makoto 2008-11-27 04:54:03

20 réponses

comme d'autres l'ont mentionné, vous voudrez probablement utiliser la classe BigDecimal , si vous voulez avoir une représentation exacte de 11.4.

maintenant, une petite explication sur la raison de ce qui se passe:

les types primitifs float et double en Java sont des nombres à virgule flottante , où le nombre est stocké comme une représentation binaire d'une fraction et d'un exposant.

plus précisément, une valeur à virgule flottante de double précision telle que double est une valeur à 64 bits, où:

  • 1 bit indique le signe (positif ou négatif).
  • 11 bits pour l'exposant.
  • 52 bits pour les chiffres significatifs (la partie fractionnaire en binaire).

ces parties sont combinées pour produire une représentation double de valeur.

(Source: Wikipédia: Double précision )

pour une description détaillée de la façon dont les valeurs à virgule flottante sont gérées en Java, voir la Section 4.2.3: types, Formats et valeurs à virgule flottante de la spécification de langage Java.

le byte , char , int , long les types sont les numéros à point fixe , qui sont des représentations exactes des nombres. Contrairement aux nombres à points fixes, les nombres à points flottants ne seront pas toujours en mesure de retourner une représentation exacte d'un nombre. C'est la raison pour laquelle vous finissez avec 11.399999999999 comme le résultat de 5.6 + 5.8 .

si vous avez besoin d'une valeur qui est exacte, comme 1.5 ou 150.1005, vous voudrez utiliser un des types de point fixe, qui sera en mesure de représenter le nombre exactement.

comme cela a été mentionné plusieurs fois déjà, Java a une classe BigDecimal qui traitera de très grands nombres et de très petits nombres.

de la référence de L'API Java pour la classe BigDecimal :

immuable, précision arbitraire décimal signé nombre. Un BigDecimal se compose d'un nombre entier de précision arbitraire non calibré valeur et une échelle entière de 32 bits. Si zéro ou positif, l'échelle est nombre de chiffres à droite de la virgule. Si le résultat est négatif, le la valeur non scalée du nombre est multiplié par dix au pouvoir du négation de la balance. La valeur de le nombre représenté par la BigDecimal est donc (unscaledValue × 10^-échelle).

il y a eu de nombreuses questions sur le débordement de la pile concernant la question des nombres à virgule flottante et sa précision. Voici une liste de questions qui peuvent être d'intérêt:

si vous voulez vraiment descendre aux détails graveleux des nombres à virgule flottante, jetez un oeil à ce que tout informaticien devrait savoir au sujet de L'arithmétique à virgule flottante .

120
répondu coobird 2017-05-23 10:31:37

lorsque vous entrez un nombre double, par exemple, 33.33333333333333 , la valeur que vous obtenez est en fait la valeur de double précision représentative la plus proche, qui est exactement:

33.3333333333333285963817615993320941925048828125

divisé par 100 donne:

0.333333333333333285963817615993320941925048828125

qui n'est pas non plus représentatif en tant que nombre à double précision, donc encore une fois il est arrondi à la valeur représentative la plus proche, qui est exactement:

0.3333333333333332593184650249895639717578887939453125

lorsque vous imprimez cette valeur, il est arrondi encore une fois à 17 chiffres décimaux, donnant:

0.33333333333333326
96
répondu Stephen Canon 2015-10-28 22:55:56

si vous voulez simplement transformer des valeurs en fractions, vous pouvez créer une classe de Fraction qui contient un champ numérateur et dénominateur.

méthodes d'Écriture pour ajouter, soustraire, multiplier et diviser ainsi qu'un toDouble méthode. De cette façon, vous pouvez éviter les flotteurs pendant les calculs.

EDIT: une mise en œuvre Rapide,

public class Fraction {

private int numerator;
private int denominator;

public Fraction(int n, int d){
    numerator = n;
    denominator = d;
}

public double toDouble(){
    return ((double)numerator)/((double)denominator);
}


public static Fraction add(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop + bTop, a.denominator * b.denominator);
    }
    else{
        return new Fraction(a.numerator + b.numerator, a.denominator);
    }
}

public static Fraction divide(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.denominator, a.denominator * b.numerator);
}

public static Fraction multiply(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.numerator, a.denominator * b.denominator);
}

public static Fraction subtract(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop-bTop, a.denominator*b.denominator);
    }
    else{
        return new Fraction(a.numerator - b.numerator, a.denominator);
    }
}

}
23
répondu Viral Shah 2016-05-26 17:14:54

observez que vous auriez le même problème si vous aviez utilisé l'arithmétique décimale de précision limitée, et a voulu traiter avec 1/3: 0.3333333 * 3 est 0.9999999, pas 1.00000000.

malheureusement, 5.6, 5.8 et 11.4 ne sont pas des nombres ronds en binaire, parce qu'ils impliquent des cinquièmes. Donc la représentation float d'eux n'est pas exacte, tout comme 0.3333 n'est pas exactement 1/3.

si tous les nombres que vous utilisez sont des décimales non récurrentes, et que vous voulez des décimales exactes résultats, utilisez BigDecimal. Ou comme d'autres l'ont dit, si vos valeurs sont comme de l'argent dans le sens où elles sont toutes un multiple de 0,01, ou 0,001, ou quelque chose, puis tout multiplier par une puissance fixe de 10 et utiliser int ou long (addition et soustraction sont triviaux: attention à la multiplication).

cependant, si vous êtes satisfait du binaire pour le calcul, mais vous voulez juste imprimer les choses dans un format légèrement plus convivial, essayez java.util.Formatter ou String.format . Dans le format chaîne de spécifier une précision inférieure à la précision d'un double. À 10 chiffres significatifs, par exemple, 11.399999999999 est 11.4, de sorte que le résultat sera presque aussi précis et plus lisible par l'homme dans les cas où le résultat binaire est très proche d'une valeur nécessitant seulement quelques décimales.

la précision à spécifier dépend un peu de combien de maths vous avez fait avec vos nombres - en général plus vous faites, plus l'erreur s'accumulera, mais certains algorithmes l'accumulent beaucoup plus rapidement que d'autres (on les appelle "instables" par opposition à "stables" en ce qui concerne les erreurs d'arrondissement). Si tout ce que vous faites est d'ajouter quelques valeurs, alors je suppose que la chute d'une seule décimale de précision va arranger les choses. Expérience.

15
répondu Steve Jessop 2008-11-27 03:23:08

vous pouvez vouloir regarder en utilisant java.mathématique.Grande classe numérique si vous avez vraiment besoin de mathématiques de précision. Voici un bon article D'Oracle / Sun sur le cas de BigDecimal . Alors que vous ne pouvez jamais représenter 1/3 comme quelqu'un a mentionné, vous peut avoir le pouvoir de décider exactement comment vous voulez que le résultat soit précis. setScale () est votre ami.. :)

Ok, parce que j'ai beaucoup trop de temps sur les mains en ce moment ici un exemple de code qui se rapporte à votre question:

import java.math.BigDecimal;
/**
 * Created by a wonderful programmer known as:
 * Vincent Stoessel
 * xaymaca@gmail.com
 * on Mar 17, 2010 at  11:05:16 PM
 */
public class BigUp {

    public static void main(String[] args) {
        BigDecimal first, second, result ;
        first = new BigDecimal("33.33333333333333")  ;
        second = new BigDecimal("100") ;
        result = first.divide(second);
        System.out.println("result is " + result);
       //will print : result is 0.3333333333333333


    }
}

et pour brancher ma nouvelle langue préférée, Groovy, voici un bel exemple de la même chose:

import java.math.BigDecimal

def  first =   new BigDecimal("33.33333333333333")
def second = new BigDecimal("100")


println "result is " + first/second   // will print: result is 0.33333333333333
8
répondu Vinny 2012-07-25 01:18:32

comme d'autres l'ont noté, toutes les valeurs décimales ne peuvent pas être représentées en binaire puisque la décimale est basée sur les puissances de 10 et binaire est basée sur les puissances de deux.

si la précision compte, utilisez BigDecimal, mais si vous voulez juste une sortie conviviale:

System.out.printf("%.2f\n", total);

vous donnera:

11.40
6
répondu Draemon 2008-11-27 03:52:24

vous auriez pu en faire un exemple. :)

si vous voulez une précision exacte, utilisez BigDecimal. Sinon, vous pouvez utiliser les ints multipliés par 10 ^ quelle que soit la précision que vous voulez.

5
répondu Dustin 2008-11-27 01:57:20

vous vous heurtez à la limite de précision du type double.

Java.Les maths ont des facilités d'arithmétique de précision arbitraire.

5
répondu John 2010-03-18 00:35:13

vous ne pouvez pas, parce que 7.3 n'a pas une représentation finie en binaire. Le plus proche que vous pouvez obtenir est 2054767329987789/2**48 = 7.3+1/1407374883553280.

regardez http://docs.python.org/tutorial/floatingpoint.html pour une explication plus détaillée. (C'est sur le site de Python, mais Java et C++ ont le même "problème".)

la solution dépend de ce que votre problème est exactement:

  • si c'est juste que vous n'aimez pas voir tous ces chiffres de bruit, alors fixez votre formatage de chaîne. N'affichez pas plus de 15 chiffres significatifs (ou 7 pour flotteur).
  • si c'est que l'inexactitude de vos nombres casse des choses comme" si", alors vous devriez écrire si (abs(x - 7.3) < tolérance) au lieu de si (x = 7.3).
  • si vous travaillez avec de l'argent, alors ce que vous voulez probablement vraiment est virgule décimale fixe. Stocker un nombre entier nombre de cents ou quelque soit la plus petite unité de votre monnaie est.
  • (très peu probable) Si vous avez besoin de plus de 53 bits significatifs (15-16 chiffres significatifs) de précision, alors utilisez un type de point flottant de haute précision, comme Bigdécimal.
5
répondu dan04 2010-03-18 02:37:52
private void getRound() {
    // this is very simple and interesting 
    double a = 5, b = 3, c;
    c = a / b;
    System.out.println(" round  val is " + c);

    //  round  val is  :  1.6666666666666667
    // if you want to only two precision point with double we 
            //  can use formate option in String 
           // which takes 2 parameters one is formte specifier which 
           // shows dicimal places another double value 
    String s = String.format("%.2f", c);
    double val = Double.parseDouble(s);
    System.out.println(" val is :" + val);
    // now out put will be : val is :1.67
}
4
répondu sravan 2012-07-11 18:28:16

utilisez java.mathématique.BigDecimal

Doubles sont des fractions binaires internes, de sorte qu'ils ne peuvent parfois pas représenter fractions décimales à la décimale exacte.

3
répondu Kevin Crowell 2010-03-17 23:15:40

multipliez tout par 100 et stockez-le en cents.

2
répondu Paul Tomblin 2008-11-27 01:56:29

les ordinateurs stockent des nombres en binaire et ne peuvent pas réellement représenter des nombres tels que 33.3333333 ou 100.0 exactement. C'est l'une des choses difficiles à propos de l'utilisation de doubles. Vous devrez juste arrondir la réponse avant de la montrer à un utilisateur. Heureusement dans la plupart des applications, vous n'avez pas besoin que beaucoup de décimales de toute façon.

2
répondu Jay Askren 2010-03-18 00:38:54

les nombres à virgule flottante diffèrent des nombres réels en ce que pour n'importe quel nombre à virgule flottante donné il y a un nombre à virgule flottante supérieur suivant. De même que les nombres entiers. Il n'y a pas de nombre entier compris entre 1 et 2.

impossible de représenter 1/3 comme flotteur. Il y a un flotteur au-dessous et il y a un flotteur au-dessus, et il y a une certaine distance entre eux. Et 1/3 est dans cet espace.

Apfloat pour Java les revendications de travailler avec une précision arbitraire flottant les nombres à virgule, mais je n'ai jamais utilisé. Sans doute la peine d'un coup d'oeil. http://www.apfloat.org/apfloat_java /

une question similaire a été posée ici avant Java à virgule flottante de haute précision de la bibliothèque

2
répondu Spike 2017-05-23 12:10:45

Doubles sont approximations des nombres décimaux dans votre source Java. Vous voyez la conséquence de l'inadéquation entre le double (qui est une valeur codée binaire) et votre source (qui est codée décimale).

Java produit l'approximation binaire la plus proche. Vous pouvez utiliser le java.texte.DecimalFormat pour afficher une meilleure valeur décimale.

1
répondu S.Lott 2008-11-27 02:22:49

utilisez un BigDecimal. Il vous permet même de spécifier des règles d'arrondissement (comme ROUND_HALF_EVEN, qui minimisera l'erreur statistique en arrondissant au voisin pair si les deux sont la même distance; i.e. à la fois 1,5 et 2,5 rond à 2).

1
répondu Adam Jaskiewicz 2008-11-27 02:36:26

Check out BigDecimal, il gère les problèmes traitant de l'arithmétique des points flottants comme ça.

le nouvel appel ressemblerait à ceci:

term[number].coefficient.add(co);

utiliser setScale() pour définir le nombre de précision de place décimale à utiliser.

0
répondu Dark Castle 2010-03-17 23:14:14

pourquoi ne pas utiliser la méthode round() de la classe de mathématiques?

// The number of 0s determines how many digits you want after the floating point
// (here one digit)
total = (double)Math.round(total * 10) / 10;
System.out.println(total); // prints 11.4
0
répondu Mr.Cat 2015-11-14 16:26:06

Si vous n'avez pas d'autre choix que d'utiliser les valeurs doubles, pouvez utiliser le code ci-dessous.

public static double sumDouble(double value1, double value2) {
    double sum = 0.0;
    String value1Str = Double.toString(value1);
    int decimalIndex = value1Str.indexOf(".");
    int value1Precision = 0;
    if (decimalIndex != -1) {
        value1Precision = (value1Str.length() - 1) - decimalIndex;
    }

    String value2Str = Double.toString(value2);
    decimalIndex = value2Str.indexOf(".");
    int value2Precision = 0;
    if (decimalIndex != -1) {
        value2Precision = (value2Str.length() - 1) - decimalIndex;
    }

    int maxPrecision = value1Precision > value2Precision ? value1Precision : value2Precision;
    sum = value1 + value2;
    String s = String.format("%." + maxPrecision + "f", sum);
    sum = Double.parseDouble(s);
    return sum;
}
0
répondu user5734382 2018-05-07 10:59:58

ne gaspillez pas votre efford en utilisant BigDecimal. Dans 99.99999% des cas, vous n'avez pas besoin. le type java double est de nature approximative mais dans presque tous les cas, il est suffisamment précis. Rappelez-vous que vous avez une erreur à 14 chiffres significatifs. C'est vraiment négligeable!

pour obtenir une sortie agréable utiliser:

System.out.printf("%.2f\n", total);
-1
répondu Maciek D. 2010-05-21 12:16:14