Pourquoi ne pas utiliser le double ou le flottant pour représenter la monnaie?

on m'a toujours dit jamais de représenter de l'argent avec double ou float types, et cette fois je vous pose la question: pourquoi?

je suis sûr qu'il ya une très bonne raison, je ne sais tout simplement pas ce que c'est.

783
demandé sur Adirio 2010-09-16 23:23:57

14 réponses

parce que les flotteurs et les doubles ne peuvent pas représenter avec précision les 10 multiples de base que nous utilisons pour l'argent. Ce problème n'est pas seulement pour Java, il est pour tout langage de programmation qui utilise des types de base 2 à virgule flottante.

en base 10, vous pouvez écrire 10.25 comme 1025 * 10 -2 (un nombre entier fois une puissance de 10). IEEE-754 nombres à virgule flottante sont différents, mais une façon très simple de penser à leur sujet est de multiplier par une puissance de deux la place. Par exemple, vous pourriez regarder 164 * 2 -4 (un nombre entier fois une puissance de deux), qui est également égale à 10.25. Ce n'est pas comme ça que les nombres sont représentés dans la mémoire, mais les implications mathématiques sont les mêmes.

même en base 10, cette notation ne peut représenter avec précision la plupart des fractions simples. Par exemple, vous ne pouvez pas représenter 1/3: la représentation décimale se répète (0.3333...), donc il n'y a pas finie d'entiers que vous pouvez multiplier par une puissance de 10 pour obtenir 1/3. Vous pourriez régler sur une longue séquence de 3 et un petit exposant, comme 333333333 * 10 -10 , mais ce n'est pas exact: si vous multipliez cela par 3, vous n'obtiendrez pas 1.

cependant, dans le but de compter de l'argent, au moins pour les pays dont l'argent est évalué dans un ordre de Grandeur du dollar US, généralement tout ce dont vous avez besoin est de pouvoir stocker des multiples de 10 -2 , de sorte qu'il n'a pas vraiment d'importance que 1/3 ne peut pas être représenté.

le problème avec les flotteurs et les doubles est que le vaste majorité de l'argent-comme des nombres n'ont pas une représentation exacte comme un entier fois un pouvoir de 2. En fait, les seuls multiples de 0,01 entre 0 et 1 (qui sont significatifs lorsqu'on traite avec de l'argent parce qu'ils sont des cents entiers) qui peuvent être représentés exactement comme un nombre binaire IEEE-754 flottant-point sont 0, 0.25, 0.5, 0.75 et 1. Tous les autres sont désactivés par un petite quantité. Par analogie avec l'exemple 0.333333, si vous prenez la valeur à virgule flottante pour 0.1 et que vous la multipliez par 10, vous n'obtiendrez pas 1.

représentant de l'argent comme un double ou float sera probablement bonne à première vue que le logiciel complète les erreurs minuscules, Mais comme vous effectuez plus d'ajouts, soustractions, multiplications et divisions sur les nombres inexacts, les erreurs se composeront et vous finirez par des valeurs qui sont visiblement pas précis. Cela rend flotteurs et doubles inadéquats pour traiter l'argent, où la précision parfaite pour les multiples de la base 10 puissances est requise.

une solution qui fonctionne dans à peu près n'importe quelle langue est d'utiliser des entiers à la place, et Compter des cents. Par exemple, 1025 $10.25. Plusieurs langues ont aussi des types intégrés pour traiter l'argent. Entre autres, Java a le BigDecimal classe, et C# a le decimal type.

807
répondu zneak 2018-06-27 23:08:58

De Bloch, J., Efficace Java, 2nd ed, Point 48:

les types float et double sont particulièrement mal adapté aux calculs parce que c'est impossible pour représenter 0,1 (ou toute autre puissance négative de dix) comme un float ou double exactement.

par exemple, supposons que vous ayez 1,03 $ et vous dépensez 42c. Combien d'argent font vous avez quitté?

System.out.println(1.03 - .42);

imprime 0.6100000000000001 .

La bonne façon de résoudre ce problème est pour utiliser BigDecimal , int ou long pour les calculs monétaires.

273
répondu dogbane 2010-09-16 19:52:08

Ce n'est pas une question de précision, il n'est ni une question de précision. Il s'agit de répondre aux attentes des humains qui utilisent la base 10 pour les calculs au lieu de la base 2. Par exemple, l'utilisation de doubles pour les calculs financiers ne produit pas de réponses "erronées" au sens mathématique du terme, mais elle peut produire des réponses qui ne sont pas ce à quoi on s'attend au sens financier.

Même si vous arrondir vos résultats à la dernière minute avant la sortie, vous pouvez toujours parfois obtenir un résultat en utilisant des doubles qui ne correspond pas aux attentes.

en utilisant une calculatrice, ou en calculant les résultats à la main, 1.40 * 165 = 231 exactement. Cependant, en interne en utilisant des doubles, sur mon environnement de compilateur / système d'exploitation, il est stocké comme un nombre binaire proche de 230.99999... donc si vous tronquez le numéro, vous obtenez 230 au lieu de 231. Vous pouvez penser que l'arrondi au lieu de la troncature aurait donné le résultat désiré de 231. C'est vrai, mais l'arrondi implique toujours la troncature. Quelle que soit la technique d'arrondissage que vous utilisez, Il ya encore des conditions limites comme celle-ci qui arrondira vers le bas quand vous vous attendez à ce qu'il arrondit. Ils sont assez rares pour qu'on ne les trouve souvent pas au moyen de tests ou d'observations fortuits. Vous devrez peut-être Écrire un code pour chercher des exemples qui illustrent des résultats qui ne se comportent pas comme prévu.

supposons que vous voulez arrondir quelque chose au centime près. Donc vous prenez votre résultat final, multiplier par 100, ajouter 0.5, tronquer, puis diviser le résultat par 100 pour revenir à pennies. Si le numéro interne que vous avez enregistré était 3.46499999.... au lieu de 3,465, vous obtiendrez 3,46 au lieu de 3,47 lorsque vous arrondirez le nombre à la pièce la plus proche. Mais vos calculs de base 10 ont peut-être indiqué que la réponse devrait être 3,465 exactement, ce qui devrait clairement arrondir à 3,47, pas à 3,46. Ce genre de choses se produit de temps en temps dans la vraie vie quand vous utilisez doubles pour les calcul. Il est rare, donc il passe souvent inaperçu comme un problème, mais il se produit.

si vous utilisez la base 10 pour vos calculs internes au lieu de doubles, les réponses sont toujours exactement ce qui est attendu par les humains, en supposant qu'aucun autre bogue dans votre code.

63
répondu Randy D Oxentenko 2012-09-12 15:11:16

je suis troublé par certaines de ces réponses. Je pense que les doubles et les flotteurs ont leur place dans les calculs financiers. Il est certain que, lors de l'addition et de la soustraction de montants monétaires non fractionnels, il n'y aura pas de perte de précision lors de l'utilisation de classes entières ou de classes BigDecimal. Mais quand vous effectuez des opérations plus complexes, vous finissez souvent avec des résultats qui sortent plusieurs ou plusieurs décimales, peu importe comment vous stockez les nombres. La question est de savoir comment vous présenter le résultat.

si votre résultat est à la limite entre être arrondi vers le haut et arrondi vers le bas, et que le dernier sou compte vraiment, vous devriez probablement dire au spectateur que la réponse est presque au milieu - en affichant plus de décimales.

le problème avec les doubles, et plus encore avec les flotteurs, est quand ils sont utilisés pour combiner de grands nombres et de petits nombres. En java,

System.out.println(1000000.0f + 1.2f - 1000000.0f);

résultats dans

1.1875
41
répondu Rob Scala 2013-04-03 14:54:24
Les flotteurs et les doubles

sont approximatifs. Si vous créez un BigDecimal et passez un float dans le constructeur vous voyez ce que le float égale réellement:

groovy:000> new BigDecimal(1.0F)
===> 1
groovy:000> new BigDecimal(1.01F)
===> 1.0099999904632568359375

ce n'est probablement pas comme ça que vous voulez représenter 1,01$.

le problème est que les spécifications de L'IEEE n'ont pas une façon de représenter exactement toutes les fractions, certaines d'entre elles finissent en fractions répétitives donc vous finissez avec des erreurs d'approximation. Depuis que les comptables aiment que les choses sortent exactement au penny, et les clients seront ennuyés s'ils paient leur facture et après que le paiement est traité ils doivent .01 et ils se font facturer des frais ou ne peuvent pas fermer leur compte, il est préférable d'utiliser les types exacts comme décimal (en C#) ou java.mathématique.BigDecimal en Java.

ce n'est pas que l'erreur n'est pas contrôlable si vous contournez: voir cet article de Peter Lawrey . C'est juste plus facile de ne pas avoir à tourner en premier lieu. La plupart des applications qui traitent l'argent ne demande pas beaucoup de maths, les opérations consistent à ajouter des choses ou allouer des sommes à différents seaux. Introduire la virgule flottante et arrondir complique les choses.

35
répondu Nathan Hughes 2013-01-29 21:26:41

le résultat du nombre de virgule flottante n'est pas exact, ce qui les rend impropres à tout calcul financier qui nécessite un résultat exact et non approximatif. float et double sont conçus pour l'ingénierie et le calcul scientifique et de nombreuses fois ne produit pas de résultat exact également résultat du calcul de la virgule flottante peut varier de JVM à JVM. Regardez ci-dessous l'exemple de BigDecimal et double primitif qui est utilisé pour représenter la valeur de l'argent, il est tout à fait clair que le point flottant le calcul peut ne pas être exact et on devrait utiliser BigDecimal pour les calculs financiers.

    // floating point calculation
    final double amount1 = 2.0;
    final double amount2 = 1.1;
    System.out.println("difference between 2.0 and 1.1 using double is: " + (amount1 - amount2));

    // Use BigDecimal for financial calculation
    final BigDecimal amount3 = new BigDecimal("2.0");
    final BigDecimal amount4 = new BigDecimal("1.1");
    System.out.println("difference between 2.0 and 1.1 using BigDecimal is: " + (amount3.subtract(amount4)));

sortie:

difference between 2.0 and 1.1 using double is: 0.8999999999999999
difference between 2.0 and 1.1 using BigDecimal is: 0.9
15
répondu Dheeraj Arora 2016-04-20 14:06:09

S'il est vrai que le type à virgule flottante ne peut représenter qu'approximativement des données décimales, il est également vrai que si l'on arrondit les nombres à la précision nécessaire avant de les présenter, on obtient le résultat correct. Généralement.

généralement parce que le type double a une précision inférieure à 16 chiffres. Si vous avez besoin d'une meilleure précision, ce n'est pas un type approprié. Les approximations peuvent aussi s'accumuler.

Il faut dire que même si vous utilisez des arithmétique de point vous avez encore à des nombres ronds, si ce n'est pour le fait que BigInteger et BigDecimal donnent des erreurs si vous obtenez des nombres décimaux périodiques. Il y a donc une approximation ici aussi.

par exemple COBOL, historiquement utilisé pour les calculs financiers, a une précision maximale de 18 chiffres. Il y a donc souvent un arrondi implicite.

concluant, à mon avis le double est inadapté principalement pour sa précision à 16 chiffres, qui peut être insuffisant, pas parce qu'il est approximatif.

considérer le résultat suivant du programme suivant. Il montre qu'après avoir arrondi double donner le même résultat que BigDecimal jusqu'à la précision 16.

Precision 14
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.000051110111115611
Double                        : 56789.012345 / 1111111111 = 0.000051110111115611

Precision 15
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.0000511101111156110
Double                        : 56789.012345 / 1111111111 = 0.0000511101111156110

Precision 16
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.00005111011111561101
Double                        : 56789.012345 / 1111111111 = 0.00005111011111561101

Precision 17
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.000051110111115611011
Double                        : 56789.012345 / 1111111111 = 0.000051110111115611013

Precision 18
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.0000511101111156110111
Double                        : 56789.012345 / 1111111111 = 0.0000511101111156110125

Precision 19
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.00005111011111561101111
Double                        : 56789.012345 / 1111111111 = 0.00005111011111561101252

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.MathContext;

public class Exercise {
    public static void main(String[] args) throws IllegalArgumentException,
            SecurityException, IllegalAccessException,
            InvocationTargetException, NoSuchMethodException {
        String amount = "56789.012345";
        String quantity = "1111111111";
        int [] precisions = new int [] {14, 15, 16, 17, 18, 19};
        for (int i = 0; i < precisions.length; i++) {
            int precision = precisions[i];
            System.out.println(String.format("Precision %d", precision));
            System.out.println("------------------------------------------------------");
            execute("BigDecimalNoRound", amount, quantity, precision);
            execute("DoubleNoRound", amount, quantity, precision);
            execute("BigDecimal", amount, quantity, precision);
            execute("Double", amount, quantity, precision);
            System.out.println();
        }
    }

    private static void execute(String test, String amount, String quantity,
            int precision) throws IllegalArgumentException, SecurityException,
            IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {
        Method impl = Exercise.class.getMethod("divideUsing" + test, String.class,
                String.class, int.class);
        String price;
        try {
            price = (String) impl.invoke(null, amount, quantity, precision);
        } catch (InvocationTargetException e) {
            price = e.getTargetException().getMessage();
        }
        System.out.println(String.format("%-30s: %s / %s = %s", test, amount,
                quantity, price));
    }

    public static String divideUsingDoubleNoRound(String amount,
            String quantity, int precision) {
        // acceptance
        double amount0 = Double.parseDouble(amount);
        double quantity0 = Double.parseDouble(quantity);

        //calculation
        double price0 = amount0 / quantity0;

        // presentation
        String price = Double.toString(price0);
        return price;
    }

    public static String divideUsingDouble(String amount, String quantity,
            int precision) {
        // acceptance
        double amount0 = Double.parseDouble(amount);
        double quantity0 = Double.parseDouble(quantity);

        //calculation
        double price0 = amount0 / quantity0;

        // presentation
        MathContext precision0 = new MathContext(precision);
        String price = new BigDecimal(price0, precision0)
                .toString();
        return price;
    }

    public static String divideUsingBigDecimal(String amount, String quantity,
            int precision) {
        // acceptance
        BigDecimal amount0 = new BigDecimal(amount);
        BigDecimal quantity0 = new BigDecimal(quantity);
        MathContext precision0 = new MathContext(precision);

        //calculation
        BigDecimal price0 = amount0.divide(quantity0, precision0);

        // presentation
        String price = price0.toString();
        return price;
    }

    public static String divideUsingBigDecimalNoRound(String amount, String quantity,
            int precision) {
        // acceptance
        BigDecimal amount0 = new BigDecimal(amount);
        BigDecimal quantity0 = new BigDecimal(quantity);

        //calculation
        BigDecimal price0 = amount0.divide(quantity0);

        // presentation
        String price = price0.toString();
        return price;
    }
}
14
répondu user1593165 2017-07-08 11:42:28

je risque d'être dévalorisé, mais je pense que l'inadéquation des nombres à virgule flottante pour le calcul des devises est surestimée. Tant que vous assurez-vous de faire les cent-arrondi correctement et avoir assez de chiffres significatifs afin de contrer le binaire-décimal représentation incompatibilité expliqué par zneak, il n'y aura pas de problème.

les personnes calculant avec la monnaie dans Excel ont toujours utilisé des flotteurs de double précision (il n'y a pas de type de monnaie dans Excel) et je n'ai pas encore vu quelqu'un se plaindre des erreurs d'arrondissement.

bien sûr, vous devez rester raisonnable; par exemple, une simple boutique en ligne ne connaîtrait probablement aucun problème avec les flotteurs de double précision, mais si vous faites par exemple la comptabilité ou tout autre chose qui nécessite l'ajout d'une grande quantité (illimitée) de nombres, vous ne voudriez pas toucher les nombres à virgule flottante avec un mât de dix pieds.

11
répondu escitalopram 2017-01-23 12:13:40

comme dit plus haut" représenter de l'argent comme un double ou un flottant sera probablement bien dans un premier temps car le logiciel complète les petites erreurs, mais comme vous effectuez plus d'additions, soustractions, multiplications et divisions sur des nombres inexacts, vous perdrez de plus en plus de précision que les erreurs s'additionnent. Cela rend les flotteurs et les doubles inadéquats pour traiter l'argent, où la précision parfaite pour les multiples de la base 10 puissances est nécessaire."

enfin Java a un méthode de travail standard avec la monnaie et L'Argent!

JSR 354: API monnaie et monnaie

JSR 354 fournit une API pour représenter, transporter et effectuer des calculs complets avec L'argent et la monnaie. Vous pouvez le télécharger à partir de ce lien:

JSR 354: API monnaie et devise télécharger

la spécification se compose: des choses suivantes:

  1. Une API pour le traitement de l'e. g. les montants et monnaies
  2. Api pour soutenir interchangeables implémentations
  3. usines pour la création d'instances des classes d'exécution
  4. fonctionnalité pour le calcul, La conversion et le formatage des montants monétaires
  5. API Java pour le travail avec de L'argent et des devises, qui est prévu pour être inclus dans Java 9.
  6. toutes les classes de spécification et les interfaces sont situées dans le javax.argent.* paquet.

exemples de JSR 354: API monnaie et monnaie:

un exemple de création D'un montant monétaire et son impression sur la console ressemble à ceci::

MonetaryAmountFactory<?> amountFactory = Monetary.getDefaultAmountFactory();
MonetaryAmount monetaryAmount = amountFactory.setCurrency(Monetary.getCurrency("EUR")).setNumber(12345.67).create();
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));

lors de l'utilisation du API de mise en œuvre de référence, le code nécessaire est beaucoup plus simple:

MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));

L'API supporte également les calculs avec les montants monétaires:

MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmount otherMonetaryAmount = monetaryAmount.divide(2).add(Money.of(5, "EUR"));

Unité de Compte et Montantmonétaire

// getting CurrencyUnits by locale
CurrencyUnit yen = MonetaryCurrencies.getCurrency(Locale.JAPAN);
CurrencyUnit canadianDollar = MonetaryCurrencies.getCurrency(Locale.CANADA);

MonetaryAmount a différentes méthodes qui permettent d'accéder à la monnaie assignée, le montant numérique, sa précision et plus:

MonetaryAmount monetaryAmount = Money.of(123.45, euro);
CurrencyUnit currency = monetaryAmount.getCurrency();
NumberValue numberValue = monetaryAmount.getNumber();

int intValue = numberValue.intValue(); // 123
double doubleValue = numberValue.doubleValue(); // 123.45
long fractionDenominator = numberValue.getAmountFractionDenominator(); // 100
long fractionNumerator = numberValue.getAmountFractionNumerator(); // 45
int precision = numberValue.getPrecision(); // 5

// NumberValue extends java.lang.Number. 
// So we assign numberValue to a variable of type Number
Number number = numberValue;

les montants monétaires peuvent être arrondis exploitant:

CurrencyUnit usd = MonetaryCurrencies.getCurrency("USD");
MonetaryAmount dollars = Money.of(12.34567, usd);
MonetaryOperator roundingOperator = MonetaryRoundings.getRounding(usd);
MonetaryAmount roundedDollars = dollars.with(roundingOperator); // USD 12.35

pour travailler avec des collections de Montantsmonétaires, quelques méthodes d'Utilités intéressantes pour le filtrage, le tri et le groupage sont disponibles.

List<MonetaryAmount> amounts = new ArrayList<>();
amounts.add(Money.of(2, "EUR"));
amounts.add(Money.of(42, "USD"));
amounts.add(Money.of(7, "USD"));
amounts.add(Money.of(13.37, "JPY"));
amounts.add(Money.of(18, "USD"));

opérations sur montants douaniers

// A monetary operator that returns 10% of the input MonetaryAmount
// Implemented using Java 8 Lambdas
MonetaryOperator tenPercentOperator = (MonetaryAmount amount) -> {
  BigDecimal baseAmount = amount.getNumber().numberValue(BigDecimal.class);
  BigDecimal tenPercent = baseAmount.multiply(new BigDecimal("0.1"));
  return Money.of(tenPercent, amount.getCurrency());
};

MonetaryAmount dollars = Money.of(12.34567, "USD");

// apply tenPercentOperator to MonetaryAmount
MonetaryAmount tenPercentDollars = dollars.with(tenPercentOperator); // USD 1.234567

ressources:

manipulation de monnaie et de devises en Java avec JSR 354

regarder dans L'argent et la monnaie de Java 9 API (JSR 354)

Voir aussi: JSR 354-monnaie et monnaie "1519220920

9
répondu Aftab Virtual 2016-08-07 07:08:51

si votre calcul comporte plusieurs étapes, l'arithmétique de précision arbitraire ne vous couvrira pas à 100%.

le seul moyen fiable d'utiliser la représentation parfaite des résultats(utiliser un type de données Fraction personnalisée qui va batch opérations de division à la dernière étape) et convertir uniquement en notation décimale dans la dernière étape.

la précision arbitraire n'aidera pas parce qu'il peut toujours y avoir des nombres qui ont tellement de places décimales, ou certains résultats tels que 0,66666666... Aucune représentation arbitraire ne couvrira le dernier exemple. Ainsi, vous aurez de petites erreurs à chaque étape.

ces erreurs s'additionneront, et risquent de ne plus être faciles à ignorer. C'est ce qu'on appelle Propagation D'erreur .

2
répondu Kemal Dağ 2015-01-05 14:56:42

la plupart des réponses ont mis en évidence les raisons pour lesquelles on ne devrait pas utiliser le double pour les calculs de monnaie et de monnaie. Et je suis totalement d'accord avec eux.

cela ne signifie pas que les doubles ne peuvent jamais être utilisés à cette fin.

j'ai travaillé sur un certain nombre de projets dont les exigences en matière de gc étaient très faibles, et le fait d'avoir de gros objets numériques a grandement contribué à ces frais généraux.

c'est le manque de compréhension sur le double représentation et manque d'expérience dans la manipulation de l'exactitude et la précision qui amène à cette sage suggestion.

vous pouvez le faire fonctionner si vous êtes en mesure de gérer les exigences de précision et de précision de votre projet, qui doit être fait sur la base de ce que la gamme de valeurs doubles est une traite.

vous pouvez vous référer à la méthode FuzzyCompare de la goyave pour obtenir plus d'idée. La tolérance du paramètre est la clé. Nous avons traité ce problème pour un nous avons fait une recherche exhaustive sur les tolérances à utiliser pour différentes valeurs numériques dans différentes gammes.

en outre, il pourrait y avoir des situations où vous êtes tenté d'utiliser des doublures comme une clé de carte avec hachage carte étant l'implémentation. C'est très risqué parce que Double.d'égal à égal et le code de hachage par exemple, les valeurs "0.5" & "de 0,6 - de 0,1" un gros gâchis.

2
répondu Dev Amitabh 2017-12-12 02:52:44

je préfère utiliser entier ou Long pour représenter la monnaie. BigDecimal bousille trop le code source.

vous devez juste savoir que toutes vos valeurs sont en cents. Ou la valeur la plus basse de la monnaie que vous utilisez.

1
répondu Tony Ennis 2010-09-18 04:47:33

plusieurs des réponses affichées à cette question traitent de L'IEEE et des normes entourant l'arithmétique à virgule flottante.

venant d'une formation en sciences autres que l'informatique (physique et ingénierie), j'ai tendance à considérer les problèmes sous un angle différent. Pour moi, la raison pour laquelle je ne voudrais pas utiliser un double ou flotter dans un calcul mathématique est que je perdrais trop d'information.

quelles sont les alternatives? Il y a beaucoup (et bien d'autres dont je ne suis pas au courant!).

BigDecimal en Java est natif du langage Java. Apfloat est une autre bibliothèque de précision arbitraire pour Java.

le type de données décimales en C# est L'alternative .NET de Microsoft pour 28 chiffres significatifs.

SciPy (Python scientifique) peut probablement aussi gérer des calculs financiers (Je n'ai pas essayé, mais je le soupçonne).

GNU Multiple Precision de la Bibliothèque (BPF) et de la GNU La bibliothèque MFPR est deux ressources libres et open-source pour C et C++.

il existe aussi des bibliothèques de précision numérique pour JavaScript (!) et je pense PHP qui peut gérer les calculs financiers.

il existe également des solutions propriétaires (en particulier, je pense, pour Fortran) et open-source ainsi que pour de nombreux langages informatiques.

je ne suis pas informaticien de formation. Cependant, j'ai tendance à pencher vers le BigDecimal en Java ou décimal en C#. Je n'ai pas essayé les autres solutions que j'ai énumérées, mais elles sont probablement très bonnes aussi.

pour moi, J'aime le BigDecimal à cause des méthodes qu'il supporte. La décimale de C#est très agréable, mais je n'ai pas eu la chance de travailler avec elle autant que je le voudrais. Je fais des calculs scientifiques d'intérêt pour moi dans mon temps libre, et BigDecimal semble fonctionner très bien parce que je peux définir la précision de mes nombres à virgule flottante. L'inconvénient de BigDecimal? Il peut être ralentissez par moments, surtout si vous utilisez la méthode divide.

vous pourriez, Pour la vitesse, regarder dans les bibliothèques libres et propriétaires en C, C++, et Fortran.

1
répondu fishermanhat 2015-07-18 01:51:36

un exemple... cela fonctionne (en fait ne pas fonctionner comme prévu), sur presque n'importe quel langage de programmation... J'ai essayé avec Delphi, VBScript, Visual Basic, JavaScript et maintenant avec Java / Android:

    double total = 0.0;

    // do 10 adds of 10 cents
    for (int i = 0; i < 10; i++) {
        total += 0.1;  // adds 10 cents
    }

    Log.d("round problems?", "current total: " + total);

    // looks like total equals to 1.0, don't?

    // now, do reverse
    for (int i = 0; i < 10; i++) {
        total -= 0.1;  // removes 10 cents
    }

    // looks like total equals to 0.0, don't?
    Log.d("round problems?", "current total: " + total);
    if (total == 0.0) {
        Log.d("round problems?", "is total equal to ZERO? YES, of course!!");
    } else {
        Log.d("round problems?", "is total equal to ZERO? NO... thats why you should not use Double for some math!!!");
    }

sortie:

round problems?: current total: 0.9999999999999999 round problems?: current total: 2.7755575615628914E-17 round problems?: is total equal to ZERO? NO... thats why you should not use Double for some math!!!

1
répondu WilliamK 2015-11-24 19:50:51