Double vs BigDecimal?

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

    Un BigDecimal est une manière exacte de représenter des nombres. Un Double a une certaine précision. Travailler avec des doubles de différentes grandeurs (disons d1=1000.0 et d2=0.001 ) pourrait entraîner une perte totale de 0.001 lors de la sum, car la différence de grandeur est si grande. Avec BigDecimal cela n’arriverait pas.

    L’inconvénient de BigDecimal est qu’il est plus lent et qu’il est un peu plus difficile de programmer les algorithmes (en raison de + - * et / leur surcharge).

    Si vous avez affaire à de l’argent, ou si la précision est indispensable, utilisez BigDecimal . Sinon, les Doubles ont tendance à être assez bons.

    Je recommande de lire le javadoc de BigDecimal car ils expliquent mieux les choses que moi ici 🙂

    Mon anglais n’est pas bon alors je vais é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 encore utiliser le double? 😉

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

    • Précision arbitraire, similaire à BigInteger, ils peuvent contenir un nombre de précision et de taille arbitraires
    • Base 10 au lieu de Base 2, un BigDecimal est n * 10 ^ échelle où n est un grand nombre entier signé arbitraire et l’échelle peut être considérée comme le nombre de chiffres pour déplacer le point décimal vers la gauche ou la droite

    La raison pour laquelle vous devriez utiliser BigDecimal pour les calculs monétaires n’est pas qu’elle peut représenter un nombre quelconque, mais qu’elle peut représenter tous les nombres pouvant être représentés en décimal et qui incluent pratiquement tous les nombres dans le monde monétaire (vous ne transférez jamais à quelqu’un).

    Si vous voulez écrire une valeur comme 1/7 comme valeur décimale, vous obtenez

     1/7 = 0.142857142857142857142857142857142857142857... 

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

    Malheureusement, les nombres tels que 1/10 ou 1/100 exprimés en nombres binarys avec une partie fractionnaire ont également un nombre infini de binarys décimaux .

     1/10 = binary 0.000110011001100110... 

    Le double stocke les valeurs sous forme de nombres binarys et peut donc introduire une erreur uniquement en convertissant un nombre décimal en un nombre binary, sans même effectuer d’arithmétique.

    Les nombres décimaux (comme BigDecimal ), d’autre part, stockent chaque chiffre décimal tel quel. Cela signifie qu’un type décimal n’est pas plus précis qu’un point binary flottant ou un point fixe dans un sens général (par exemple, il ne peut pas stocker 1/7 sans perte de précision), mais il est plus précis pour les nombres donnés avec un nombre fini de chiffres décimaux, comme c’est souvent le cas pour les calculs d’argent.

    Java BigDecimal a l’avantage supplémentaire de pouvoir avoir un nombre arbitraire (mais fini) de chiffres des deux côtés du point décimal, limité uniquement par la mémoire disponible.

    BigDecimal est la bibliothèque numérique de précision arbitraire d’Oracle. BigDecimal fait partie du langage Java et est utile pour une variété d’applications allant du financier au scientifique.

    Il n’y a rien de mal à utiliser des doubles pour certains calculs. Supposons cependant que vous vouliez calculer Math.Pi * Math.Pi / 6, c’est-à-dire la valeur de la fonction Riemann Zeta pour un argument réel de deux (un projet sur lequel je travaille actuellement). La division en virgule flottante présente un problème douloureux d’erreur d’arrondi.

    BigDecimal, d’autre part, inclut de nombreuses options pour calculer des expressions avec une précision arbitraire. Les méthodes d’ajout, de multiplication et de division décrites dans la documentation Oracle ci-dessous “prennent la place” de +, * et / dans BigDecimal Java World:

    http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html

    La méthode compareTo est particulièrement utile dans les boucles while et for.

    Attention toutefois à l’utilisation des constructeurs pour BigDecimal. Le constructeur de chaîne est très utile dans de nombreux cas. Par exemple, le code

    BigDecimal onethird = new BigDecimal (“0.33333333333”);

    utilise une représentation de chaîne de 1/3 pour représenter ce nombre répétitif à un degré de précision spécifié. L’erreur d’arrondi est probablement si profonde quelque part dans la JVM que les erreurs d’arrondi ne perturberont pas la plupart de vos calculs pratiques. Par expérience personnelle, j’ai cependant constaté que les arrondis s’accentuaient. La méthode setScale est importante à cet égard, comme le montre la documentation Oracle.

     package j2ee.java.math; /** * Generated from IDL definition of "valuetype "BigDecimal"" * TomORB IDL comstackr v1.0 */ public abstract class BigDecimal extends j2ee.java.lang.Number implements org.omg.CORBA.portable.StreamableValue, j2ee.java.lang.Comparable { private Ssortingng[] _truncatable_ids = {"RMI:java.math.BigDecimal:11F6D308F5398BBD:54C71557F981284F"}; protected int scale_; protected j2ee.java.math.BigInteger intVal; /* constants */ int ROUND_UP = 0; int ROUND_DOWN = 1; int ROUND_CEILING = 2; int ROUND_FLOOR = 3; int ROUND_HALF_UP = 4; int ROUND_HALF_DOWN = 5; int ROUND_HALF_EVEN = 6; int ROUND_UNNECESSARY = 7; public abstract int _hashCode(); public abstract int scale(); public abstract int signum(); public abstract boolean _equals(org.omg.CORBA.Any arg0); public abstract java.lang.Ssortingng _toSsortingng(); public abstract j2ee.java.math.BigDecimal abs(); public abstract j2ee.java.math.BigDecimal negate(); public abstract j2ee.java.math.BigDecimal movePointLeft(int arg0); public abstract j2ee.java.math.BigDecimal movePointRight(int arg0); public abstract j2ee.java.math.BigDecimal setScale(int arg0); public abstract j2ee.java.math.BigDecimal setScale(int arg0, int arg1); public abstract j2ee.java.math.BigDecimal valueOf(long arg0); public abstract j2ee.java.math.BigDecimal valueOf(long arg0, int arg1); public abstract int compareTo(j2ee.java.math.BigDecimal arg0); public abstract j2ee.java.math.BigInteger toBigInteger(); public abstract j2ee.java.math.BigInteger unscaledValue(); public abstract j2ee.javax.rmi.CORBA.ClassDesc classU0024(java.lang.Ssortingng arg0); public abstract j2ee.java.math.BigDecimal add(j2ee.java.math.BigDecimal arg0); public abstract j2ee.java.math.BigDecimal max(j2ee.java.math.BigDecimal arg0); public abstract j2ee.java.math.BigDecimal min(j2ee.java.math.BigDecimal arg0); public abstract j2ee.java.math.BigDecimal multiply(j2ee.java.math.BigDecimal arg0); public abstract j2ee.java.math.BigDecimal subtract(j2ee.java.math.BigDecimal arg0); public abstract j2ee.java.math.BigDecimal divide(j2ee.java.math.BigDecimal arg0, int arg1); public abstract j2ee.java.math.BigDecimal divide(j2ee.java.math.BigDecimal arg0, int arg1, int arg2); public void _write (org.omg.CORBA.portable.OutputStream os) { super._write( os ); os.write_long(scale_); ((org.omg.CORBA_2_3.portable.OutputStream)os).write_value( new java.lang.Ssortingng("intVal") ); } public void _read (final org.omg.CORBA.portable.InputStream os) { super._read( os ); scale_=os.read_long(); intVal=(j2ee.java.math.BigInteger)((org.omg.CORBA_2_3.portable.InputStream)os).read_value ( "RMI:java.math.BigInteger:E2F79B6E7A470003:8CFC9F1FA93BFB1D".toSsortingng() ); } public Ssortingng[] _truncatable_ids() { return _truncatable_ids; } public org.omg.CORBA.TypeCode _type() { return j2ee.java.math.BigDecimalHelper.type(); } } 

    Les types numériques primitifs sont utiles pour stocker des valeurs uniques en mémoire. Cependant, en ce qui concerne les calculs utilisant des types double et flottant, il y a un problème avec l’arrondi. Cela se produit car la représentation de la mémoire ne correspond pas exactement à la valeur. Par exemple, une valeur double est supposée prendre 64 bits, mais Java n’utilise pas tous les 64 bits. Elle ne stocke que ce qu’elle pense des parties importantes du nombre. Ainsi, vous pouvez arriver à des valeurs erronées lorsque vous ajoutez des valeurs du type float ou double. Peut-être que cette vidéo https://youtu.be/EXxUSz9x7BM expliquera plus