Quel est le meilleur type de données à utiliser pour de l'argent en C #?

426

Quel est le meilleur type de données à utiliser pour de l'argent en C #?

NotDan
la source
4
Vous pourriez trouver les réponses de ce post utiles.
ntombela
Voici un mappage pour tous les types de données: docs.microsoft.com/en-us/dotnet/framework/data/adonet/…
JohnLBevan
De plus, si vous utilisez des annotations de données, incluez using System.ComponentModel.DataAnnotations;... [DataType(DataType.Currency)] msdn.microsoft.com/en-us/library/…
JohnLBevan

Réponses:

422

Comme il est décrit à la décimale comme:

Le mot clé décimal indique un type de données 128 bits. Comparé aux types à virgule flottante, le type décimal a plus de précision et une plage plus petite, ce qui le rend approprié pour les calculs financiers et monétaires .

Vous pouvez utiliser une décimale comme suit:

decimal myMoney = 300.5m;
Lee Treveil
la source
41
Vous devez expliquer en quoi ce lien est important. Une réponse doit être suffisamment bonne en elle-même, avec un lien comme référence ou détail supplémentaire. Voir stackoverflow.com/help/how-to-answer
TheRubberDuck
2
Ainsi, la réponse de longueur minimale peut contenir moins de caractères que le commentaire de longueur minimale - intéressant! Non pas que j'ai un problème avec la réponse concise / concise, surtout quand elle est également "profonde" en ce qu'elle est liée à une discussion plus approfondie.
B. Clay Shannon
3
Réponse incroyable, et je ne pense pas qu'elle ait besoin d'explications supplémentaires car elle répond complètement à la question. Le lien vers la documentation MSDN est un bonus en ce qui me concerne. Bravo!
trnelson
@Leee Treveil, à quoi ressemble l'argent (9.0098) signifie 4 caractères après le point
SAR
114

System.Decimal

Le type de valeur décimale représente des nombres décimaux allant de positif 79.228.162.514.264.337.593.543.950.335 à négatif 79.228.162.514.264.337.593.543.950.335. Le type de valeur décimale convient aux calculs financiers nécessitant un grand nombre de chiffres entiers et fractionnaires significatifs et aucune erreur d'arrondi. Le type décimal n'élimine pas la nécessité d'arrondir. Au contraire, il minimise les erreurs dues à l'arrondissement.

Je voudrais souligner cette excellente réponse de zneak sur la raison pour laquelle le double ne devrait pas être utilisé.

David Walschots
la source
68

Utilisez le modèle Money de Patterns of Enterprise Application Architecture ; spécifiez le montant sous forme décimale et la devise sous forme d'énumération.

lmsasu
la source
2
J'allais en fait le suggérer, mais je fais de la devise une classe afin de pouvoir définir un taux de change (par rapport à une "devise de base", souvent le dollar américain [que j'ai fixé pour avoir un taux de change de 1,00]).
Thomas Owens
5
Pour les futurs visiteurs de ce fil (comme moi), il y a maintenant ceci: nuget.org/packages/Money et ça bouge !
Korijn
Vous vous demandez si un tel type doit être une structure ou une classe. Une énumération décimale + an (int) lui donne 20 octets. Mon argent est encore en structure.
nawfal
Ce Moneynuget a un lien github mort pour le site du projet alors ... pas de documents?
George Mauer
Le problème est que si vous créez votre propre implémentation, vous devez trouver comment la conserver. Et l'ORM (EF) le plus populaire ne prend pas du tout en charge les types de données personnalisés. Par conséquent, on demande à quelqu'un d'aller très profondément dans les mauvaises herbes pour faire ce qui devrait être une chose assez simple.
George Mauer
25

Décimal. Si vous choisissez le double, vous vous exposez aux erreurs d'arrondi

SquidScareMe
la source
8
@Jess doublepeut introduire des erreurs d'arrondi car la virgule flottante ne peut pas représenter exactement tous les nombres (par exemple, 0,01 n'a pas de représentation exacte en virgule flottante). Decimal, D'autre part, ne représentent des nombres exactement . (Le compromis Decimala une plage plus petite que la virgule flottante) La virgule flottante peut vous donner des erreurs d'arrondi * par inadvertance (par exemple 0.01+0.01 != 0.02). Decimalpeut vous donner des erreurs d'arrondi, mais uniquement lorsque vous l'avez demandé (par exemple, Math.Round(0.01+0.02)renvoie zéro)
Ian Boyd
2
@IanBoyd: La valeur "1,57 $" peut être représentée avec précision (double) 157. Si l'on utilise doubleet applique soigneusement l'échelle et l'arrondi spécifique au domaine le cas échéant, cela peut être parfaitement précis. Si l'on est bâclé dans son arrondi, cela decimalpeut donner des résultats sémantiquement incorrects (par exemple, si l'on additionne plusieurs valeurs qui sont censées être arrondies au centime le plus proche, mais qui ne les entourent pas en premier). La seule bonne chose decimalest que la mise à l'échelle est intégrée.
supercat
1
@supercat, concernant ce commentaire "si l'on additionne plusieurs valeurs qui sont censées être arrondies au centime le plus proche, mais qui ne les entoure pas en premier", je ne vois pas comment un flotteur résoudrait cela. Il s'agit d'une erreur de l'utilisateur et n'a rien à voir avec les décimales à mon humble avis. j'obtiens le point mais je pense qu'il a été mal placé, principalement parce que IanBoyd l'a spécifié ... si vous le demandez.
sawe
13

Acceptez le modèle Money: la gestion des devises est tout simplement trop lourde lorsque vous utilisez des décimales.

Si vous créez une classe Currency, vous pouvez alors y mettre toute la logique relative à l'argent, y compris une méthode ToString () correcte, plus de contrôle des valeurs d'analyse et un meilleur contrôle des divisions.

De plus, avec une classe Currency, il n'y a aucune chance de mélanger involontairement de l'argent avec d'autres données.

Lennaert
la source
10

Une autre option (surtout si vous faites rouler votre propre classe) consiste à utiliser un int ou un int64, et à désigner les quatre chiffres inférieurs (ou peut-être même 2) comme «à droite de la virgule décimale». Donc, "sur les bords", vous aurez besoin de "* 10000" à l'entrée et de "/ 10000" à la sortie. Il s'agit du mécanisme de stockage utilisé par SQL Server de Microsoft, voir http://msdn.microsoft.com/en-au/library/ms179882.aspx

La particularité est que toute votre sommation peut être faite en utilisant l'arithmétique d'entier (rapide).

dsz
la source
7

La plupart des applications avec lesquelles j'ai travaillé decimalreprésentent l'argent. Ceci est basé sur l'hypothèse que l'application ne sera jamais concernée par plus d'une devise.

Cette hypothèse peut être basée sur une autre hypothèse, selon laquelle l'application ne sera jamais utilisée dans d'autres pays avec des devises différentes. J'ai vu des cas où cela s'est avéré faux.

Maintenant, cette hypothèse est remise en question d'une nouvelle manière: les nouvelles devises telles que le Bitcoin sont de plus en plus courantes et ne sont spécifiques à aucun pays. Il n'est pas irréaliste qu'une application utilisée dans un seul pays doive encore prendre en charge plusieurs devises.

Certaines personnes diront que créer ou même utiliser un type juste pour de l'argent est un "placage à l'or" ou ajouter une complexité supplémentaire au-delà des exigences connues. Je suis fortement en désaccord. Plus un concept est omniprésent dans votre domaine, plus il est important de faire un effort raisonnable pour utiliser l'abstraction correcte à l'avance. Si vous voulez voir la complexité, essayez de travailler dans une application qui utilisait autrefois decimalet maintenant il y a une Currencypropriété supplémentaire à côté de chaque decimalpropriété.

Si vous utilisez la mauvaise abstraction à l'avance, le remplacer plus tard sera cent fois plus de travail. Cela signifie potentiellement introduire des défauts dans le code existant, et la meilleure partie est que ces défauts impliqueront probablement des sommes d'argent, des transactions avec de l'argent ou tout simplement quelque chose avec de l'argent.

Et ce n'est pas si difficile d'utiliser autre chose que décimal. Google "nuget money type" et vous verrez que de nombreux développeurs ont créé de telles abstractions (y compris moi.) C'est facile. C'est aussi simple que d'utiliser DateTimeau lieu de stocker une date dans un fichier string.

Scott Hannen
la source
5

Créez votre propre classe. Cela semble étrange, mais un type .Net est insuffisant pour couvrir différentes devises.

Noel Kennedy
la source