Dans un simple jeu de simulation d'entreprise (construit en Java + Slick2D), le montant actuel d'argent d'un joueur doit-il être stocké sous la forme d'un float
ou d'un int
autre élément?
Dans mon cas d'utilisation, la plupart des transactions utilisent des cents (0,50 USD, 1,20 USD, etc.), et le calcul du taux d'intérêt simple est impliqué.
J'ai vu des gens dire que vous ne devriez jamais utiliser float
pour de l'argent, ainsi que des gens qui disent que vous ne devriez jamais utiliser int
pour de l'argent. Je pense que je devrais utiliser int
et arrondir tout calcul de pourcentage nécessaire. Que devrais-je utiliser?
Currency
type semblable à celui de Delphi, qui utilise des mathématiques à virgule décimale mises à l’échelle pour vous donner des calculs décimaux sans les problèmes de précision inhérents à la virgule flottante?BigDecimal
ce genre de problèmes.Réponses:
Vous pouvez utiliser
int
et considérer tout en cents. 1,20 $ ne représente que 120 cents. À l'affichage, vous mettez la décimale à l'endroit auquel elle appartient.Les calculs d'intérêts seraient simplement tronqués ou arrondis. Alors
De cette façon, vous n'avez pas de décimales en désordre qui traînent toujours. Vous pourriez devenir riche en ajoutant l'argent non comptabilisé (en raison d'arrondissements) à votre propre compte bancaire.
la source
round
il n'y a pas de vraie raison de jeterint
, est-ce qu'il y a?Ok, je vais sauter dedans.
Mon conseil: c'est un jeu. Prenez le facile et utilisez
double
.Voici ma raison d'être:
float
Un problème de précision apparaît lors de l’ajout d’unités à des millions, alors, même s’il est bénin, j’éviterais ce type.double
commence seulement à avoir des problèmes autour des quintillons (un milliard de milliards).int
etlong
inutile parce que vous ne savez pas où arrêter avec les décimales.BigDecimal
vous donnera une précision arbitraire mais deviendra péniblement lente, à moins que vous ne la fixiez à un moment donné. Quelles sont les règles d'arrondi? Comment choisissez-vous où vous arrêter? Vaut-il la peine d'avoir plus de bits de précision quedouble
? Je ne crois pas.La raison pour laquelle l'arithmétique en virgule fixe est utilisée dans les applications financières est parce qu'elles sont déterministes. Les règles d'arrondi sont parfaitement définies, parfois par la loi, et doivent être strictement appliquées, mais l'arrondi se produit toujours à un moment donné. Tout argument en faveur d'un type donné basé sur la précision est vraisemblablement faux. Tous les types ont des problèmes de précision avec le type de calcul que vous allez faire.
Exemples pratiques
Je vois pas mal de commentaires affirmant des choses sur l’arrondissement ou la précision avec lesquelles je ne suis pas d’accord. Voici quelques exemples supplémentaires pour illustrer ce que je veux dire.
Stockage : si votre unité de base est le cent, vous voudrez probablement arrondir au cent le plus proche lors du stockage des valeurs:
Lors de l'adoption de cette méthode, vous n'aurez absolument aucun problème d'arrondi, ce que vous n'auriez pas aussi avec un type entier.
Récupération : tous les calculs peuvent être effectués directement sur les valeurs doubles, sans conversions:
Notez que le code ci-dessus ne fonctionne pas si vous remplacez
double
parint64_t
: il y aura une conversion implicite versdouble
, puis une troncature avecint64_t
, avec une perte possible de information.data.GetValue ()Comparaison : les comparaisons sont la seule chose à faire avec les types à virgule flottante. Je suggère d'utiliser une méthode de comparaison telle que celle-ci:
Arrondir l'équité
Supposons que le compte compte 9,99 $ avec un intérêt de 4%. Combien doit gagner le joueur? Avec un arrondi entier, vous obtenez 0,03 $; avec des arrondis en virgule flottante, vous obtenez 0,04 $. Je crois que ce dernier est plus juste.
la source
int_64t
s.10/3 = 3+3+4
ou10/3 = 3+3+3 (+1)
. Pour toutes les autres opérations, les entiers fonctionnent parfaitement, contrairement à la virgule flottante qui peut poser des problèmes d'arrondi dans toutes les opérations.Les types à virgule flottante en Java (
float
,double
) ne sont pas une bonne représentation des devises pour une raison principale: une erreur machine lors de l'arrondi. Même si un simple calcul retourne un nombre entier - comme12.0/2
(6.0), la virgule flottante peut l’entourer de façon erronée (du fait de la représentation spécifique de ces types en mémoire) par6.0000000000001
ou5.999999999999998
ou similaire. Cela résulte de l'arrondi spécifique de la machine qui se produit dans le processeur et est propre à l'ordinateur qui l'a calculé. Habituellement, il est rarement un problème d’opérer avec ces valeurs, car l’erreur est assez négligente, mais c’est difficile de l’afficher à l’utilisateur.Une solution possible à ce problème consisterait à utiliser une implémentation personnalisée de type de données à virgule flottante, telle que
BigDecimal
. Il prend en charge de meilleurs mécanismes de calcul qui isolent au moins les erreurs d'arrondi pour ne pas être spécifiques à la machine, mais sont plus lentes en termes de performances.Si vous avez besoin d'une productivité élevée, vous feriez mieux de vous en tenir aux types simples. Si vous travaillez avec des données financières importantes et que chaque centime est important (comme une application Forex ou un jeu de casino), je vous recommanderais d'utiliser
Long
oulong
.Long
vous permettrait de gérer de grandes quantités et une bonne précision. Supposons simplement que vous avez besoin, disons, de 4 chiffres après la virgule, il vous suffit de multiplier ce montant par 10000. Ayant de l'expérience dans le développement de jeux de casino en ligne, je l'aiLong
souvent vue utilisée pour représenter l'argent en cents . Dans les applications Forex, la précision est plus importante, vous aurez donc besoin d’un multiplicateur plus important. Néanmoins, les nombres entiers ne posent aucun problème d’arrondi machine (bien sûr, l’arrondissement manuel comme dans 3/2, vous devriez vous en occuper vous-même).Une option acceptable serait d'utiliser les types de virgule flottante standard -
Float
etDouble
, si les performances sont plus importantes que la précision au centième de cent près. Ensuite, dans votre logique d’affichage, il vous suffit d’utiliser une mise en forme prédéfinie , afin que la laideur d’un arrondi potentiel de la machine n’atteigne pas l’utilisateur.la source
Pour le jeu à petite échelle et où la vitesse de traitement, la mémoire est un problème important (en raison de la précision ou de travailler avec un calculateur mathématique peut rendre péniblement lent), il suffit de doubler .
Mais pour les jeux à grande échelle (par exemple, les jeux sociaux) et où la vitesse de traitement, la mémoire n'est pas limitée, BigDecimal est meilleur. Parce qu'ici,
Ressources:
De https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency
De Bloch, J., Effective Java, 2 e éd., Article 48:
Regardez aussi
la source
Vous voulez stocker votre monnaie
long
et calculer votre monnaiedouble
, au moins comme une sauvegarde. Vous voulez que toutes les transactions aient lieu commelong
.La raison pour laquelle vous souhaitez stocker votre devise
long
est que vous ne voulez pas perdre de devise.Supposons que vous utilisiez un
double
, et que vous n’ayez pas d’argent. Quelqu'un vous donne trois centimes, puis les reprend.Eh bien, ce n'est pas si cool. Peut-être que quelqu'un avec 10 $ veut donner sa fortune en vous donnant d'abord trois centimes, puis en donnant 9,70 $ à quelqu'un d'autre.
Et puis vous leur rendez les dix sous:
Ceci est juste cassé.
Utilisons maintenant un long terme, et nous garderons une trace des dixièmes de centimes (donc 1 = 0,001 $). Donnons à chacun sur la planète un milliard, cent douze millions, soixante quinze mille mille cent quarante trois dollars:
Euh, attendez, on peut donner à tout le monde plus d'un milliard de dollars, et ne dépenser qu'un peu plus de deux? Le débordement est une catastrophe ici.
Ainsi, chaque fois que vous calculez une somme d’argent à transférer, utilisez-la
double
etMath.round
obtenez-lalong
. Puis corrigez les soldes (ajoutez et soustrayez les deux comptes) en utilisantlong
.Votre économie ne coulera pas et atteindra un quadrillion de dollars.
Il ya plus de problèmes épineux - par exemple, que faites-vous si vous effectuez 20 paiements? * - mais cela devrait vous aider à démarrer.
* Vous calculez ce qu'est un paiement, arrondissez à
long
; puis multiplier par20.0
et vérifier qu'il est dans la fourchette; Dans ce cas, multipliez le paiement par20L
pour obtenir le montant déduit de votre solde. En général, toutes les transactions doivent être traitées commelong
si, de sorte que vous avez vraiment besoin de résumer toutes les transactions individuelles; vous pouvez multiplier comme un raccourci, mais vous devez vous assurer de ne pas ajouter des erreurs d' arrondi et que vous ne déborde pas, ce qui signifie que vous devez vérifier avecdouble
avant de faire le calcul réellong
.la source
J'irais jusqu'à dire que toute valeur pouvant être affichée à l'utilisateur devrait presque toujours être un entier. L'argent n'en est que l'exemple le plus marquant. Infliger 225 dégâts à quatre reprises à un monstre de 900 CV et constater qu'il lui reste 1 HP restera soustrait à l'expérience, tout autant que de constater que vous êtes une fraction invisible d'un centime de moins.
Sur le plan plus technique, je pense qu’il est intéressant de noter qu’il n’est pas nécessaire de recourir à des actions flottantes pour faire des choses avancées comme l’intérêt. Tant que vous avez suffisamment de marge dans le type entier choisi, une multiplication et une division remplacent une multiplication par un nombre décimal, par exemple, pour ajouter 4%, arrondi à la valeur inférieure:
Pour ajouter 4%, arrondi par les conventions standard:
Ici, pas d’imprécision, les arrondis se divisant toujours exactement sur la
.5
marque.Edit, la vraie question:
Voyant à quel point le débat est parti , je commence à penser que la question décrivant ce que est tout au sujet peut être plus utile qu'un simple
int
/float
réponse. Au coeur de la question, il ne s'agit pas de types de données, mais de contrôler les détails d'un programme.L'utilisation d'un entier pour représenter une valeur non entière oblige le programmeur à traiter les détails de la mise en œuvre. "Quelle précision utiliser?" et "Quelle manière de tourner?" sont des questions auxquelles il faut répondre explicitement.
Un float par contre ne force pas le programmeur à s’inquiéter, il fait déjà à peu près tout ce à quoi on pourrait s’attendre. Mais comme un flotteur n’est pas une précision infinie, il se produira des arrondis, et ces arrondis sont assez imprévisibles.
Que se passe-t-il dans une utilisation flotte et veut contrôler l'arrondi? Cela s'avère presque impossible. Le seul moyen de rendre un flottant réellement prévisible est d'utiliser uniquement des valeurs pouvant être représentées dans un entier
2^n
. Mais cette construction rend les flotteurs assez difficiles à utiliser.La réponse à la question simple est donc la suivante: si vous voulez prendre le contrôle, utilisez des entiers, sinon utilisez des flottants.
Mais la question débattue n’est qu’une autre forme de la question: voulez-vous prendre le contrôle?
la source
Même si c'est "seulement un jeu", j'utiliserais Money Pattern de Martin Fowler, appuyé par un long.
Pourquoi?
Localisation (L10n): en utilisant ce modèle, vous pouvez facilement localiser la devise de votre jeu. Pensez aux vieux jeux de magnats comme "Transport Tycoon". Ils permettent facilement au joueur de changer la monnaie du jeu (par exemple, de la livre sterling au dollar américain) pour rencontrer la monnaie du monde réel.
Et
Cela signifie que vous pouvez stocker 9 000 fois la masse monétaire actuelle de M2 US (environ 10 000 milliards de dollars). Vous laissant assez de place pour utiliser n'importe quelle autre devise mondiale, probablement même ceux qui ont / ont eu une hyperinflation (Si curieux, voir l'inflation allemande après la Première Guerre mondiale , où 1 livre de pain représentait 3 000 000 000 de marks)
Long est facile à conserver, très rapide et devrait vous laisser assez de temps pour faire tous les calculs d'intérêts en utilisant uniquement des arithmétiques entiers, réponse de eBusiness donne une explication sur la façon de procéder.
la source
Combien de travail voulez-vous mettre à ce sujet? Quelle est l'importance de la précision? Vous souciez-vous de suivre les erreurs fractionnelles qui se produisent avec l'arrondi et l'imprécision de la représentation des nombres décimaux dans un système binaire?
En fin de compte, j'ai tendance à passer un peu plus de temps à coder et à mettre en œuvre des tests unitaires pour les "cas critiques" et les cas problématiques connus - je penserais donc à un BigInteger modifié qui englobe des quantités arbitrairement grandes et conserve les bits fractionnels avec un BigDecimal ou une partie BigRational (deux BigIntegers, un pour le dénominateur et une autre pour le numérateur) - et incluez du code pour conserver la partie fractionnelle en tant que fraction réelle en ajoutant (peut-être seulement périodiquement) toute partie non fractionnaire au BigInteger principal. Ensuite, je garderais un suivi interne de tout ce qui était en cents, juste pour garder les parties fractionnaires en dehors des calculs de l'interface graphique.
Probablement trop complexe pour un (simple) jeu - mais bon pour une bibliothèque publiée en open source! Nous aurions juste besoin de trouver des moyens de conserver de bonnes performances lorsque nous traitons les bits fractionnaires ...
la source
Certainement pas flotter. Avec seulement 7 chiffres, vous n’avez que la précision suivante:
12 345,67 123 456,7x
Vous voyez donc qu'avec déjà 10 ^ 5 dollars, vous perdez la trace de quelques centimes. double est utilisable pour le jeu, pas dans la vie réelle pour des raisons de précision.
Mais long (et par là, je veux dire 64 bits, ou long en C ++) ne suffit pas si vous voulez suivre les transactions et les résumer. Il suffit de maintenir les avoirs nets de toute entreprise, mais toutes les transactions d'un an risquent de déborder. Cela dépend de la taille de votre "monde" financier.
la source
Une autre solution que j'ajouterai ici est de créer une classe monétaire. La classe serait quelque chose comme (pourrait même simplement être une structure).
Cela vous permettrait de représenter les centimes dans ce cas sous forme d'entier et les dollars entiers sous forme d'entier. Puisque vous avez un indicateur distinct pour être négatif, vous pouvez utiliser des entiers non signés pour vos valeurs, ce qui double le montant possible (vous pouvez également écrire une classe de nombres qui applique cette idée aux nombres pour obtenir des nombres VRAIMENT grands). Tout ce que vous devez faire est de surcharger les opérateurs mathématiques et vous pouvez l'utiliser comme n'importe quel type de données. Cela prend plus de mémoire, mais cela élargit vraiment les limites de valeur.
Cela pourrait être étendu pour inclure des éléments tels que le nombre d'unités partielles par unités entières afin que vous puissiez avoir des devises subdivisées sur autre chose que 100 sous-unités, dans ce cas, cents par dollar. Vous pourriez avoir 125 flops par exemple un floper.
Modifier:
J'élargirai l'idée en ce sens que vous pourriez également avoir une table de correspondance, à laquelle la classe monétaire peut avoir accès, qui fournirait un taux de change pour sa devise par rapport à toutes les autres devises. Vous pouvez intégrer cela dans les fonctions de surcharge de votre opérateur. Par conséquent, si vous essayez automatiquement d'ajouter 4 USD à 4 £ britanniques, cela vous donnera automatiquement 6,6 livres (au moment de l'écriture).
la source
Comme mentionné dans l'un des commentaires sur votre question initiale, ce problème a été couvert dans StackExchange: https://stackoverflow.com/questions/8148684/what-is-the-best-data-type-to-use-for- money-in-java-app
Fondamentalement, les types à virgule flottante ne doivent jamais être utilisés pour représenter des devises et, comme la plupart des langages, Java a un type plus approprié. La propre documentation d'Oracle sur les primitives suggère d'utiliser BigDecimal .
la source
Utilisez la classe c'est la meilleure façon. Vous pouvez masquer la mise en œuvre de votre argent, vous pouvez basculer le double / long tout ce que vous voulez à tout moment.
Exemple
Dans votre code, utilisez simplement le getter, c’est une meilleure façon de penser et vous pouvez stocker des méthodes plus utiles.
la source