353 votes

Double ou BigDecimal ?

Je dois calculer des variables à virgule flottante et mon collègue me suggère d'utiliser BigDecimal au lieu de double car il sera plus précis. Mais je veux savoir ce que c'est et comment en tirer le meilleur parti. BigDecimal ?

0 votes

514voto

extraneon Points 13362

A BigDecimal est une manière exacte de représenter les nombres. A Double a une certaine précision. Travailler avec des doubles de différentes magnitudes (par exemple d1=1000.0 y d2=0.001 ) pourrait entraîner le 0.001 sont abandonnées lors de la sommation, car la différence de magnitude est très importante. Avec BigDecimal cela n'arriverait pas.

L'inconvénient de BigDecimal c'est qu'elle est plus lente et qu'il est un peu plus difficile de programmer des algorithmes de cette manière (en raison de + - * y / ne pas être surchargé).

Si vous traitez de l'argent, ou si la précision est de rigueur, utilisez BigDecimal . Sinon, Doubles ont tendance à être suffisantes.

Je vous recommande de lire le javadoc de BigDecimal car ils expliquent les choses mieux que je ne le fais ici :)

0 votes

Oui, je calcule le prix d'une action et je pense que BigDecimal est utile dans ce cas.

5 votes

@Truong Ha : Lorsque vous travaillez avec des prix, vous voulez utiliser BigDecimal. Et si vous les stockez dans la base de données, vous voulez quelque chose de similaire.

119 votes

Dire que "BigDecimal est une manière exacte de représenter les nombres" est trompeur. 1/3 et 1/7 ne peuvent pas être exprimés exactement dans un système numérique en base 10 (BigDecimal) ou en base 2 (float ou double). 1/3 peut être exprimé exactement en base 3, base 6, base 9, base 12, etc. et 1/7 peut être exprimé exactement en base 7, base 14, base 21, etc. Les avantages de BigDecimal sont qu'il s'agit d'une précision arbitraire et que les humains sont habitués aux erreurs d'arrondi que l'on obtient en base 10.

218voto

Basil Points 2030

Mon anglais n'est pas bon, alors je vais juste écrire un exemple simple ici.

double a = 0.02;
double b = 0.03;
double c = b - a;
System.out.println(c);

BigDecimal _a = new BigDecimal("0.02");
BigDecimal _b = new BigDecimal("0.03");
BigDecimal _c = _b.subtract(_a);
System.out.println(_c);

Sortie du programme :

0.009999999999999998
0.01

Quelqu'un veut-il encore utiliser le double ? ;)

11 votes

@eldjon Ce n'est pas vrai, regardez cet exemple : BigDecimal two = new BigDecimal("2") ; BigDecimal eight = new BigDecimal("8") ; System.out.println(two.divide(eight)) ; Cela donne 0,25.

4 votes

Double forevr :D

0 votes

Néanmoins, si vous utilisez un flottant à la place, vous obtenez la même précision que BigDecimal dans ce cas, mais des performances bien meilleures.

70voto

Meros Points 116

Il y a deux différences principales par rapport au double :

  • Précision arbitraire, comme les BigInteger, ils peuvent contenir des nombres de précision et de taille arbitraires (alors qu'un double a un nombre fixe de bits).
  • en base 10 au lieu de la base 2, un BigDecimal est n*10^-scale où n est un grand nombre entier signé arbitraire et l'échelle peut être considérée comme le nombre de chiffres permettant de déplacer le point décimal vers la gauche ou la droite.

Il n'est toujours pas vrai de dire que BigDecimal peut représenter n'importe quel nombre. Mais il y a deux raisons pour lesquelles vous devriez utiliser BigDecimal pour les calculs monétaires :

  • Il peut représenter tous les nombres qui peuvent être représentés en notion décimale et cela inclut pratiquement tous les nombres dans le monde monétaire (vous ne transférez jamais 1/3 $ à quelqu'un).
  • La précision peut être contrôlée pour éviter les erreurs accumulées. Avec un double En effet, à mesure que l'amplitude de la valeur augmente, sa précision diminue, ce qui peut introduire une erreur importante dans le résultat.

5 votes

Cette réponse explique vraiment la différence et la raison d'utiliser BigDecimal plutôt que double. Les problèmes de performance sont secondaires.

0 votes

Ce n'est pas entièrement vrai. Vous avez écrit qu'un BigDecimal est "n*10^scale". Java ne fait cela que pour les nombres négatifs. Donc, ce qui est correct, c'est : "unscaledValue × 10^échelle". Pour les nombres positifs, le BigDecimal est constitué d'une "valeur entière de précision arbitraire non mise à l'échelle et d'une échelle entière de 32 bits", tandis que l'échelle est le nombre de chiffres à droite du point décimal.

0 votes

@Meros - pouvez-vous préciser : "précision arbitraire" ?

38voto

Si vous écrivez une valeur fractionnaire comme 1 / 7 en valeur décimale, vous obtenez

1/7 = 0.142857142857142857142857142857142857142857...

avec une séquence infinie de 142857 . Comme vous ne pouvez écrire qu'un nombre fini de chiffres, vous introduirez inévitablement une erreur d'arrondi (ou de troncature).

Des chiffres comme 1/10 o 1/100 exprimés sous forme de nombres binaires avec une partie fractionnaire ont également un nombre infini de chiffres après le point décimal :

1/10 = binary 0.0001100110011001100110011001100110...

Doubles stockent les valeurs en binaire et peuvent donc introduire une erreur uniquement en convertissant un nombre décimal en un nombre binaire, sans même effectuer d'arithmétique.

Les nombres décimaux (comme BigDecimal ), en revanche, stocke chaque chiffre décimal tel quel (codé en binaire, mais chaque chiffre décimal est indépendant). Cela signifie qu'un type décimal n'est pas plus précis qu'un type binaire à virgule flottante ou à virgule fixe dans un sens général (c'est-à-dire qu'il ne peut stocker 1/7 sans perte de précision), mais elle est plus précise pour les nombres qui ont un nombre fini de chiffres décimaux, comme c'est souvent le cas pour les calculs monétaires.

Java BigDecimal a l'avantage supplémentaire de pouvoir avoir un nombre arbitraire (mais fini) de chiffres de part et d'autre de la virgule, limité uniquement par la mémoire disponible.

22voto

jfajunior Points 106

Si vous avez affaire à des calculs, il existe des lois sur la manière dont vous devez calculer et sur la précision que vous devez utiliser. Si vous ne respectez pas ces règles, vous commettez une infraction. La seule vraie raison est que la représentation binaire des cas décimaux n'est pas précise. Comme le dit simplement Basil, un exemple est la meilleure explication. Pour compléter son exemple, voici ce qui se passe :

static void theDoubleProblem1() {
    double d1 = 0.3;
    double d2 = 0.2;
    System.out.println("Double:\t 0,3 - 0,2 = " + (d1 - d2));

    float f1 = 0.3f;
    float f2 = 0.2f;
    System.out.println("Float:\t 0,3 - 0,2 = " + (f1 - f2));

    BigDecimal bd1 = new BigDecimal("0.3");
    BigDecimal bd2 = new BigDecimal("0.2");
    System.out.println("BigDec:\t 0,3 - 0,2 = " + (bd1.subtract(bd2)));
}

Sortie :

Double:  0,3 - 0,2 = 0.09999999999999998
Float:   0,3 - 0,2 = 0.10000001
BigDec:  0,3 - 0,2 = 0.1

Nous avons aussi ça :

static void theDoubleProblem2() {
    double d1 = 10;
    double d2 = 3;
    System.out.println("Double:\t 10 / 3 = " + (d1 / d2));

    float f1 = 10f;
    float f2 = 3f;
    System.out.println("Float:\t 10 / 3 = " + (f1 / f2));

    // Exception! 
    BigDecimal bd3 = new BigDecimal("10");
    BigDecimal bd4 = new BigDecimal("3");
    System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4)));
}

Nous donne la sortie :

Double:  10 / 3 = 3.3333333333333335
Float:   10 / 3 = 3.3333333
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion

Mais :

static void theDoubleProblem2() {
    BigDecimal bd3 = new BigDecimal("10");
    BigDecimal bd4 = new BigDecimal("3");
    System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4, 4, BigDecimal.ROUND_HALF_UP)));
}

A la sortie :

BigDec:  10 / 3 = 3.3333

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X