Meilleur moyen de stocker les valeurs de devise en C ++

Je sais qu’un flottant n’est pas approprié pour stocker des valeurs de devise en raison des erreurs d’arrondi. Existe-t-il un moyen standard de représenter l’argent en C ++?

J’ai regardé dans la bibliothèque de boost et je n’ai rien trouvé à ce sujet. En Java, il semble que BigInteger soit le moyen mais je n’ai pas trouvé d’équivalent en C ++. Je pourrais écrire ma propre classe d’argent, mais je préfère ne pas le faire si quelque chose est testé.

Ne le stockez pas en cents, car vous accumulerez des erreurs en multipliant rapidement vos impôts et vos intérêts. Conservez au minimum deux chiffres significatifs: 12,45 $ seraient stockés à 124 500. Si vous le conservez dans un entier signé de 32 bits, vous aurez 200 000 $ à utiliser (positif ou négatif). Si vous avez besoin d’un plus grand nombre ou d’une plus grande précision, un entier 64 bits signé vous donnera probablement tout l’espace dont vous aurez besoin pendant longtemps.

Il peut être utile d’emballer cette valeur dans une classe, de vous donner une place pour créer ces valeurs, y faire de l’arithmétique et les formater pour les afficher. Cela vous donnerait également une place centrale à transporter autour de quelle devise elle est stockée (USD, CAD, EURO, etc.).

Après avoir traité cela dans des systèmes financiers réels, je peux vous dire que vous voulez probablement utiliser un nombre avec au moins 6 décimales de précision (en supposant que l’USD). J’espère que, puisque vous parlez de valeurs en devises, vous ne serez pas déçu. Il y a des propositions pour append des types décimaux à C ++, mais je n’en connais aucun qui existe actuellement.

Le meilleur type C ++ natif à utiliser ici serait long double.

Le problème avec d’autres approches qui utilisent simplement un int est que vous devez stocker plus que vos centimes. Souvent, les transactions financières sont multipliées par des valeurs non entières et cela va vous causer des problèmes, car 100,25 $ traduits en 10025 * 0,000123523 (par exemple, APR) posent problème. Vous allez finir par se retrouver dans des terrains à virgule flottante et les conversions vont vous coûter cher.

Maintenant, le problème ne se produit pas dans la plupart des situations simples. Je vais vous donner un exemple précis:

Compte tenu de plusieurs milliers de valeurs monétaires, si vous multipliez chacune par un pourcentage et que vous les ajoutez ensuite, vous vous retrouverez avec un nombre différent de si vous aviez multiplié le total par ce pourcentage si vous ne gardez pas assez de décimales. Maintenant, cela peut fonctionner dans certaines situations, mais vous aurez souvent plusieurs sous en moins. Dans mon expérience générale, assurez-vous de conserver une précision pouvant atteindre 6 décimales (en vous assurant que la précision restante est disponible pour la partie entière).

Comprenez également que peu importe le type avec lequel vous le stockez si vous faites des calculs de manière moins précise. Si votre calcul se fait en simple précision, cela n’a pas d’importance si vous le stockez en double précision. Votre précision sera correcte au calcul le moins précis.


Cela dit, si vous ne faites aucun calcul autre que l’addition ou la soustraction simple, puis stockez le nombre, alors tout ira bien, mais dès que quelque chose de plus complexe apparaît, vous allez avoir des problèmes.

Regardez la bibliothèque mathématique à virgule flottante décimale Intelr relativement récente. Il est spécifiquement destiné aux applications financières et implémente certaines des nouvelles normes pour l’arithmétique à virgule flottante binary (IEEE 754r) .

Le plus gros problème est de se dépasser!

19% de 42,50 € = 8 075 €. En raison des règles allemandes pour arrondir cela est de 8,08 €. Le problème est que (au moins sur ma machine) 8 075 ne peuvent pas être représentés en double. Même si je change la variable dans le débogueur à cette valeur, je me retrouve avec 8,0749999 ….

Et c’est là que ma fonction d’arrondi (et toute autre sur la logique à virgule flottante que je peux imaginer) échoue car elle produit 8,07 €. Le chiffre significatif est 4 et la valeur est donc arrondie au chiffre inférieur. Et c’est tout à fait faux et vous ne pouvez rien y faire à moins d’éviter d’utiliser des valeurs à virgule flottante autant que possible.

Cela fonctionne très bien si vous représentez 42,50 € comme Integer 42500000.

42500000 * 19/100 = 8075000. Vous pouvez maintenant appliquer la règle d’arrondi au-dessus de 8080000. Cela peut facilement être transformé en une valeur de devise pour des raisons d’affichage. 8,08 €.

Mais je mettrais toujours ça dans une classe.

Je suggère que vous gardiez une variable pour le nombre de cents au lieu de dollars. Cela devrait éliminer les erreurs d’arrondi. L’affichage dans le format standard dollars / cents devrait être une préoccupation de vue.

Quel que soit le type que vous choisissez, je vous recommanderais de le mettre dans un “typedef” pour pouvoir le changer à un autre moment.

Connaissez votre gamme de données.

Un flotteur ne convient que pour 6 à 7 chiffres de précision, ce qui signifie un maximum d’environ + -9999,99 sans arrondir. C’est inutile pour la plupart des applications financières.

Un double est bon pour 13 chiffres, donc: + -99 999 999 999,99, toujours être prudent lors de l’utilisation de grands nombres. Reconnaître que la soustraction de deux résultats similaires supprime une grande partie de la précision (voir un livre sur l’parsing numérique pour les problèmes potentiels).

Un nombre entier de 32 bits est bon à +2Billions (la mise à l’échelle des centimes diminuera de 2 décimales)

Entier 64 bits gérera tout argent, mais encore une fois, soyez prudent lorsque vous convertissez et multipliez par divers taux dans votre application qui pourrait être flottante / double.

La clé est de comprendre votre domaine de problème. Quelles exigences légales avez-vous pour l’exactitude? Comment allez-vous afficher les valeurs? À quelle fréquence la conversion aura-t-elle lieu? Avez-vous besoin d’internationalisation? Assurez-vous de pouvoir répondre à ces questions avant de prendre votre décision.

Vous pouvez essayer le type de données décimal:

https://github.com/vpiotr/decimal_for_cpp

Conçu pour stocker des valeurs orientées vers l’argent (solde monétaire, taux de change, taux d’intérêt), précision définie par l’utilisateur. Jusqu’à 19 chiffres.

C’est une solution en-tête uniquement pour C ++.

Cela dépend des exigences de votre entreprise en matière d’arrondissement. Le moyen le plus sûr est de stocker un nombre entier avec la précision requirejse et de savoir quand et comment appliquer l’arrondi.

Vous dites que vous avez regardé dans la bibliothèque de boost et que vous n’avez rien trouvé à ce sujet. Mais là vous avez multiprecision / cpp_dec_float qui dit:

La base de ce type est 10. Par conséquent, elle peut se comporter subtilement différemment des types base-2.

Donc, si vous utilisez déjà Boost, cela devrait être bon pour les valeurs et les opérations des devises, car son nombre de base 10 et sa précision de 50 ou 100 chiffres (beaucoup).

Voir:

#include  #include  #include  int main() { float bogus = 1.0 / 3.0; boost::multiprecision::cpp_dec_float_50 correct = 1.0 / 3.0; std::cout << std::setprecision(16) << std::fixed << "float: " << bogus << std::endl << "cpp_dec_float: " << correct << std::endl; return 0; } 

Sortie:

float: 0.3333333432674408

cpp_dec_float: 0.3333333333333333

* Je ne dis pas que float (base 2) est mauvais et décimal (base 10) est bon. Ils se comportent juste différemment ...

** Je sais que c'est un ancien post et boost :: multiprecision a été introduit en 2013, alors je voulais le remarquer ici.

Entiers, stockez-les toujours en cents (ou quelle que soit la devise que vous programmez). Le problème est que, peu importe ce que vous faites avec les virgules flottantes, vous trouverez une situation où le calcul sera différent si vous le faites. en virgule flottante. L’arrondi à la dernière minute n’est pas la solution, car les calculs en devise réelle sont arrondis au fur et à mesure.

Vous ne pouvez pas non plus éviter le problème en modifiant l’ordre des opérations. Cela échoue lorsque vous avez un pourcentage qui vous laisse sans représentation binary correcte. Les comptables vont paniquer si vous êtes par un seul centime.

Je recommanderais d’utiliser un long int pour stocker la devise dans la plus petite dénomination (par exemple, l’argent américain serait des cents), si une devise décimale est utilisée.

Très important: assurez-vous de nommer toutes les valeurs de votre devise en fonction de ce qu’elles contiennent réellement. (Exemple: account_balance_cents) Cela évitera beaucoup de problèmes.

(Un autre exemple est celui des pourcentages. Ne nommez jamais une valeur “XXX_percent” quand elle contient un ratio non multiplié par cent.)

La bibliothèque GMP a des implémentations “bignum” que vous pouvez utiliser pour des calculs d’entiers de taille arbitraire, nécessaires pour gérer l’argent. Voir la documentation de mpz_class ( attention : ceci est horriblement incomplet, une gamme complète d’opérateurs arithmétiques est fournie) .

Une option consiste à stocker 10.01 $ en 1001, et à faire tous les calculs en centimes, en divisant par 100D lorsque vous affichez les valeurs.

Ou, utilisez des flotteurs et seulement des tours au dernier moment possible.

Les problèmes peuvent souvent être atténués en modifiant l’ordre des opérations.

Au lieu de la valeur * 0,10 pour un rabais de 10%, utilisez (valeur * 10) / 100, ce qui vous aidera beaucoup. (rappelez-vous que .1 est un binary répété)

Notre institution financière utilise “double”. Comme nous sums un magasin à “revenu fixe”, nous avons beaucoup d’algorithmes compliqués qui utilisent le double de toute façon. L’astuce consiste à s’assurer que la présentation de votre utilisateur ne dépasse pas la précision du double. Par exemple, lorsque nous avons une liste de transactions totalisant des billions de dollars, nous devons nous assurer que nous n’imprimons pas de déchets en raison de problèmes d’arrondi.

allez-y et écrivez votre propre argent ( http://junit.sourceforge.net/doc/testinfected/testing.htm ) ou devise () selon ce dont vous avez besoin. et le tester.

La solution est simple, stockez-la avec la précision requirejse, sous forme d’entier décalé. Mais lors de la lecture en converti en double flotteur, les erreurs d’arrondi sont moins fréquentes. Ensuite, lorsque vous stockez dans la firebase database, multipliez-la à la précision du nombre entier, mais avant de la tronquer, ajoutez +/- 1/10 pour compenser les erreurs de troncature ou +/- 51/100 pour arrondir. Peasy facile.

J’utiliserais longtemps signé pour 32 bits et signé longtemps pour 64 bits. Cela vous donnera une capacité de stockage maximale pour la quantité sous-jacente elle-même. Je développerais ensuite deux manipulateurs personnalisés. Celui qui convertit cette quantité en fonction des taux de change et une autre qui formate cette quantité dans votre devise de choix. Vous pouvez développer plus de manipulateurs pour différentes opérations / règles financières.

Stockez le montant en dollars et en cent sous forme de deux nombres entiers distincts.