Question

    

Cette question a déjà une réponse ici:

    
            
  •              Conserver la précision avec double en Java                                      20 réponses                          
  •     
    

On dirait que la soustraction déclenche un problème et que la valeur obtenue est fausse.

double tempCommission = targetPremium.doubleValue()*rate.doubleValue()/100d;

78,75 = 787,5 * 10,0 / 100d

double netToCompany = targetPremium.doubleValue() - tempCommission;

708,75 = 787,5 - 78,75

double dCommission = request.getPremium().doubleValue() - netToCompany;

877.8499999999999 = 1586,6 - 708,75

La valeur attendue résultante serait de 877,85.

Que faut-il faire pour assurer le bon calcul?

Était-ce utile?

La solution

Pour contrôler la précision de l'arithmétique en virgule flottante, vous devez utiliser java.math.BigDecimal . Lisez Le besoin de BigDecimal de John Zukowski pour plus d'informations.

Etant donné votre exemple, la dernière ligne serait la suivante avec BigDecimal.

import java.math.BigDecimal;

BigDecimal premium = BigDecimal.valueOf("1586.6");
BigDecimal netToCompany = BigDecimal.valueOf("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

Cela produit le résultat suivant.

877.85 = 1586.6 - 708.75

Autres conseils

Comme indiqué dans les réponses précédentes, il s’agit d’une conséquence de l’arithmétique en virgule flottante.

Comme suggéré dans un précédent poster, lorsque vous effectuez des calculs numériques, utilisez java.math.BigDecimal.

Cependant, il est difficile d’utiliser BigDecimal. Lorsque vous convertissez une valeur double en BigDecimal(double), vous pouvez utiliser un nouveau constructeur BigDecimal.valueOf(double) ou la méthode de fabrique statique double. Utilisez la méthode fabrique statique.

Le double constructeur convertit l'intégralité de la précision de String en <=> tandis que la fabrique statique la convertit effectivement en <=>, puis la convertit en <=>.

Cela devient pertinent lorsque vous rencontrez ces erreurs d’arrondis subtiles. Un nombre peut afficher 0,55, mais sa valeur interne est '0,5849999999999999996447286321199499070644378662109375'. Si vous utilisiez le constructeur <=>, vous obtiendriez un nombre NON égal à 0,585, tandis que la méthode statique vous attribuerait une valeur égale à 0,585.

double value = 0.585;
System.out.println(new BigDecimal(value));
System.out.println(BigDecimal.valueOf(value));

sur mon système donne

0.58499999999999996447286321199499070644378662109375
0.585

Autre exemple:

double d = 0;
for (int i = 1; i <= 10; i++) {
    d += 0.1;
}
System.out.println(d);    // prints 0.9999999999999999 not 1.0

Utilisez BigDecimal à la place.

EDIT:

En outre, il convient de souligner que ce n'est pas un problème d'arrondi "Java". Exposition d'autres langues comportement similaire (mais pas nécessairement cohérent). Java garantit au moins un comportement cohérent à cet égard.

Je modifierais l'exemple ci-dessus comme suit:

import java.math.BigDecimal;

BigDecimal premium = new BigDecimal("1586.6");
BigDecimal netToCompany = new BigDecimal("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

Ainsi, vous évitez les pièges de l'utilisation de chaîne pour commencer. Une autre alternative:

import java.math.BigDecimal;

BigDecimal premium = BigDecimal.valueOf(158660, 2);
BigDecimal netToCompany = BigDecimal.valueOf(70875, 2);
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

Je pense que ces options sont meilleures que l’utilisation de doubles. Dans Webapps, les chiffres commencent quand même comme des chaînes.

Chaque fois que vous effectuez des calculs avec des doubles, cela peut arriver. Ce code vous donnerait 877.85:

double réponse = Math.round (dCommission * 100000) / 100000.0;

Économisez le nombre de centimes plutôt que de dollars, et faites simplement le format en dollars lorsque vous le sortez. De cette façon, vous pouvez utiliser un entier qui ne souffre pas des problèmes de précision.

Consultez les réponses à cette . Ce que vous voyez est essentiellement une conséquence naturelle de l’utilisation de l’arithmétique en virgule flottante.

Vous pouvez choisir une précision arbitraire (chiffres significatifs de vos entrées?) et arrondir votre résultat à celle obtenue, si vous vous sentez à l'aise pour le faire.

C’est une question amusante.

L'idée derrière la réponse Timons est de spécifier un epsilon qui représente la plus petite précision qu'un double juridique puisse être. Si vous savez dans votre application que vous n'aurez jamais besoin d'une précision inférieure à 0,00000001, il suggère alors qu'il est suffisant pour obtenir un résultat plus précis très proche de la vérité. Utile dans les applications où ils connaissent leur précision maximale (pour financer par exemple des précisions sur les devises, etc.)

Cependant, le problème fondamental en essayant d’arrondir les résultats est que, lorsque vous divisez par un facteur pour le redimensionner, vous créez une autre possibilité pour les problèmes de précision. Toute manipulation de doublons peut introduire des problèmes d’imprécision avec une fréquence variable. Surtout si vous essayez d’arrondir à un chiffre très significatif (si vos opérandes sont & Lt; 0), par exemple si vous exécutez ce qui suit avec le code Timons:

System.out.println(round((1515476.0) * 0.00001) / 0.00001);

Entraînera 1499999.9999999998 là où le but ici est d'arrondir aux unités de 500 000 (c'est-à-dire que nous voulons 1500 000)

En fait, le seul moyen de vous assurer que vous avez éliminé l'imprécision consiste à passer par un BigDecimal pour le réduire. par exemple

System.out.println(BigDecimal.valueOf(1515476.0).setScale(-5, RoundingMode.HALF_UP).doubleValue());

En combinant la stratégie epsilon et la stratégie BigDecimal, vous aurez un contrôle précis sur votre précision. L’idée étant qu’epsilon vous rapproche beaucoup et que BigDecimal élimine toute imprécision causée par une nouvelle mise à l’échelle. Bien que l'utilisation de BigDecimal réduise les performances attendues de votre application.

Il m’a été signalé que la dernière étape consistant à utiliser BigDecimal pour redimensionner l’échelle n’est pas toujours nécessaire pour certains cas d’utilisation lorsque vous pouvez déterminer qu’il n’existe aucune valeur en entrée indiquant que la division finale peut réintroduire une erreur. Actuellement, je ne sais pas comment le déterminer correctement. Si quelqu'un sait comment, je serais ravi d'en entendre parler.

Jusqu'à présent, le moyen le plus élégant et le plus efficace de le faire en Java:

double newNum = Math.floor(num * 100 + 0.5) / 100;

Mieux encore, utilisez JScience , car BigDecimal est assez limité (par exemple, aucun sqrt fonction)

double dCommission = 1586.6 - 708.75;
System.out.println(dCommission);
> 877.8499999999999

Real dCommissionR = Real.valueOf(1586.6 - 708.75);
System.out.println(dCommissionR);
> 877.850000000000
double rounded = Math.rint(toround * 100) / 100;

Bien que vous ne devriez pas utiliser les doubles pour des calculs précis, le truc suivant m'a aidé si vous arrondissez les résultats de toute façon.

public static int round(Double i) {
    return (int) Math.round(i + ((i > 0.0) ? 0.00000001 : -0.00000001));
}

Exemple:

    Double foo = 0.0;
    for (int i = 1; i <= 150; i++) {
        foo += 0.00010;
    }
    System.out.println(foo);
    System.out.println(Math.round(foo * 100.0) / 100.0);
    System.out.println(round(foo*100.0) / 100.0);

Quelles impressions:

0.014999999999999965
0.01
0.02

Plus d'infos: http://en.wikipedia.org/wiki/Double_precision

C'est assez simple.

Utilisez l'opérateur% .2f pour la sortie. Problème résolu!

Par exemple:

int a = 877.8499999999999;
System.out.printf("Formatted Output is: %.2f", a);

Le code ci-dessus donne une impression de: 877,85

L'opérateur% .2f définit que seules DEUX décimales doivent être utilisées.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top