58 votes

Multiplication de BigDecimal par zéro

J'effectue une simple multiplication avec BigDecimal et j'ai constaté un comportement étrange lors de la multiplication par zéro (la multiplication par zéro est correcte dans ce cas d'utilisation).

Les mathématiques de base me disent que tout ce qui est multiplié par zéro est égal à zéro (voir : Propriété du produit zéro y Propriétés de la multiplication )

Cependant, le code suivant échouera systématiquement avec la même erreur :

assertEquals(new BigDecimal(0), new BigDecimal(22.3).multiply(new BigDecimal(0)));
java.lang.AssertionError: 
Expected :0
Actual   :0E-48

S'agit-il d'une imprécision de BigDecimal ou d'une branche spécialisée des mathématiques qui m'échappe ?

Notes : JDK 1.6.0_27 fonctionnant dans IntelliJ 11

84voto

Keppil Points 28356

Vous ne pouvez pas utiliser le equals() méthode de comparaison BigDecimals comme le fait cette affirmation. C'est parce que cette fonction equals va comparer l'échelle . Si l'échelle est différente, equals() retournera false, même s'il s'agit du même nombre mathématiquement.

Vous pouvez toutefois utiliser compareTo() pour faire ce que vous voulez :

Comme l'indique @assylias, vous devriez également utiliser la fonction new BigDecimal("22.3") pour éviter les problèmes de double précision.

BigDecimal expected = BigDecimal.ZERO;
BigDecimal actual = new BigDecimal("22.3").multiply(BigDecimal.ZERO);
assertEquals(0, expected.compareTo(actual));

Il existe également une méthode appelée signum() qui renvoie -1, 0 ou 1 pour négatif, zéro et positif. Vous pouvez donc également tester le zéro avec

assertEquals(0, actual.signum());

47voto

assylias Points 102015

Il y a deux problèmes avec votre code :

  • vous devriez comparer BigDecimal avec compareTo au lieu de equals, comme conseillé par les autres réponses
  • mais vous devez également utiliser le constructeur de chaînes de caractères : new BigDecimal("22.3") au lieu du constructeur double new BigDecimal(22.3) pour éviter les problèmes de double précision

En d'autres termes, le code suivant (qui utilise correctement compareTo) renvoie toujours false :

BigDecimal bd = new BigDecimal(0.1).multiply(new BigDecimal(10));
System.out.println(bd.compareTo(BigDecimal.ONE) == 0);

parce que 0.1d * 10d != 1

20voto

Jigar Joshi Points 116533

equals() en BigDecimal vérifie l'état interne de BigDecimal pour comparaison

Voir le code ci-dessous

public boolean equals(Object x) {
    if (!(x instanceof BigDecimal))
        return false;
    BigDecimal xDec = (BigDecimal) x;
    if (x == this)
        return true;
    if (scale != xDec.scale)
        return false;
    long s = this.intCompact;
    long xs = xDec.intCompact;
    if (s != INFLATED) {
        if (xs == INFLATED)
            xs = compactValFor(xDec.intVal);
        return xs == s;
    } else if (xs != INFLATED)
        return xs == compactValFor(this.intVal);

    return this.inflate().equals(xDec.inflate());
}

si vous voulez comparer les valeurs, utilisez compareTo()

Changez votre code en

assertEquals(0 , new BigDecimal(0).compareTo(new BigDecimal(22.3).multiply(new BigDecimal(0)));

Mise à jour :

Utilisez le constructeur prenant une chaîne de caractères comme paramètre pour BigDecimal. Pour plus de précision, consultez les liens suivants


Voir aussi

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