Existe-t-il une représentation de données unique qui fonctionne pour toutes les devises (même celles différentes des dollars, des euros et des livres)?

12

Je peux trouver de nombreuses questions sur les bibliothèques à utiliser pour représenter des montants dans une certaine devise. Et sur la question séculaire de pourquoi vous ne devriez pas stocker de devises sous forme de nombre à virgule flottante IEEE 754. Mais je n'arrive pas à trouver autre chose. Il y a sûrement beaucoup plus à savoir sur la monnaie dans l'utilisation du monde réel. Je suis particulièrement intéressé par ce que vous devez savoir pour le représenter dans un usage physique (par exemple, avec le dollar, vous n'avez jamais une précision inférieure à 0,01 $, permettant la représentation sous la forme d'un nombre entier de cents).

Mais il est difficile de faire des hypothèses sur la polyvalence de votre programme lorsque les seules devises que vous connaissez sont les devises occidentales populaires (telles que le dollar, l'euro et la livre). Quelles sont les autres connaissances pertinentes dans une perspective purement programmatique? Je ne suis pas préoccupé par le sujet de la conversion.

En particulier, que devons-nous savoir pour pouvoir stocker des valeurs dans une certaine devise et les imprimer?

Kat
la source
2
Tous les programmeurs ne travaillent pas dans des secteurs qui manipulent des montants en devises.
whatsisname
4
Oh bien sûr. Mais cela peut être dit à peu près n'importe quoi. Je pense que nous pouvons supposer que ceux qui trouveraient cette question utile seraient ceux qui travaillent avec la monnaie.
Kat
5
J'ai effectué de nombreux travaux de développement pour des banques et d'autres sociétés de services financiers à New York. Et je peux à peu près vous garantir qu'il n'y a pas de réponse "fermée" à votre question. Vous pourriez tout aussi bien demander: "Combien de mathématiques dois-je savoir?" Par exemple, dans les années 1990, les cours des actions sont passés des huitièmes et 256ièmes à la décimale, nécessitant beaucoup de reprogrammation. Qui aurait pu deviner que cela arriverait? Il vous suffit de comprendre l'entreprise que vos applications prennent en charge et la meilleure façon de représenter les données (dans ce cas, la devise) pour répondre à ces besoins.
John Forkosh
4
Mon 0.02: Le prix est une chose. La monnaie en est une autre.
Machado
3
J'ai pensé: Oh, il doit y avoir un dénominateur commun pour toutes les devises. Mais peut-être pas. Le penny sterling est actuellement de 1⁄100 de livre, mais était de 1⁄240 de livre. Donc, non seulement vous avez une division de 1/240, mais vous avez une date de basculement, et peut-être un taux de change pour l'ancien pence non encore basculé. en.wikipedia.org/wiki/Penny_sterling Ne me lancez même pas sur les coquilles de cauris! en.wikipedia.org/wiki/Shell_money ou le fait que l'argent nécessite la croyance pour devenir réel: en.wikipedia.org/wiki/Money Excellente question même si elle ne correspond pas bien à StackExchange!
GlenPeterson

Réponses:

24

par exemple, avec le dollar, vous n'avez jamais une précision inférieure à 0,01 $

Oh vraiment? entrez la description de l'image ici

le problème séculaire de la raison pour laquelle vous ne devriez pas stocker de devise sous forme de nombre à virgule flottante IEEE 754.

entrez la description de l'image ici

N'hésitez pas à stocker les pouces en nombres à virgule flottante IEEE 754 . Ils stockent précisément ce que vous attendez.

N'hésitez pas à stocker toute somme d'argent en nombres à virgule flottante IEEE 754 que vous pouvez stocker en utilisant les graduations qui divisent une règle en fractions de pouce.

Pourquoi? Parce que lorsque vous utilisez IEEE 754, c'est comme ça que vous le stockez.

Le problème avec les pouces, c'est qu'ils sont divisés en deux. La chose à propos de la plupart des types de devises est qu'elles sont divisées en dixièmes (certaines ne le sont pas, mais restons concentrés).

Cette différence ne serait pas si confuse, sauf que, pour la plupart des langages de programmation, l'entrée et la sortie des nombres à virgule flottante IEEE 754 sont exprimées en décimales! Ce qui est très étrange car ils ne sont pas stockés en décimales.

Pour cette raison, vous ne voyez jamais comment les bits font des choses étranges lorsque vous demandez à l'ordinateur de les stocker 0.1. Vous ne voyez l'étrangeté que lorsque vous faites des calculs contre cela et il y a d'étranges erreurs.

De la java efficace de Josh Bloch :

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

Produit 0.6100000000000001

Ce qui est le plus révélateur, ce n'est pas le 1chemin assis là-bas à droite. Ce sont les chiffres étranges qui ont dû être utilisés pour l'obtenir. Plutôt que d'utiliser l'exemple le plus populaire 0.1, nous devons utiliser un exemple qui montre le problème et évite l'arrondi qui le cacherait.

Par exemple, pourquoi cela fonctionne-t-il?

System.out.println(.01 - .02);

Produit -0.01

Parce que nous avons eu de la chance.

Je déteste les problèmes difficiles à diagnostiquer car j'ai parfois la «chance».

IEEE 754 ne peut tout simplement pas stocker 0,1 avec précision. Mais si vous lui demandez de stocker 0,1 puis de l'imprimer, il affichera 0,1 et vous penserez que tout va bien. Ce n'est pas bien, mais vous ne pouvez pas le voir car il est arrondi pour revenir à 0,1.

Certaines personnes confondent les autres en appelant ces écarts des erreurs d'arrondi. Non, ce ne sont pas des erreurs d'arrondi. L'arrondi consiste à faire ce qu'il est censé faire et à transformer ce qui n'est pas un nombre décimal en un nombre décimal afin qu'il puisse s'imprimer à l'écran.

Mais cela masque l'inadéquation entre la façon dont le numéro est affiché et la façon dont il est stocké. L'erreur ne s'est pas produite lors de l'arrondi. Cela s'est produit lorsque vous avez décidé de mettre un nombre dans un système qui ne peut pas le stocker avec précision et que vous avez supposé qu'il était stocké précisément quand il ne l'était pas.

Personne ne s'attend à ce que π soit stocké avec précision dans une calculatrice et ils parviennent à l'utiliser très bien. Le problème n'est donc même pas lié à la précision. Il s'agit de la précision attendue. Les ordinateurs affichent un dixième de 0.1la même manière que nos calculatrices, nous nous attendons donc à ce qu'ils stockent un dixième parfaitement comme le font nos calculatrices. Ils ne le font pas. Ce qui est surprenant, car les ordinateurs sont plus chers.

Permettez-moi de vous montrer le décalage:

entrez la description de l'image ici

Notez que 1/2 et 0,5 s'alignent parfaitement. Mais 0,1 ne correspond tout simplement pas. Bien sûr, vous pouvez vous rapprocher si vous continuez à diviser par 2, mais vous ne le frapperez jamais exactement. Et nous avons besoin de plus en plus de bits chaque fois que nous divisons par 2. Donc, représenter 0,1 avec tout système qui divise par 2 a besoin d'un nombre infini de bits. Mon disque dur n'est pas si gros.

Donc , IEEE 754 cesse d' essayer quand il court en bits. Ce qui est bien car j'ai besoin de place sur mon disque dur pour ... des photos de famille. Pas vraiment. Photos de famille. : P

Quoi qu'il en soit, ce que vous tapez et ce que vous voyez sont les décimales (à droite) mais ce que vous stockez sont les bicimales (à gauche). Parfois, ce sont parfaitement les mêmes. Parfois non. Parfois, il semble qu'ils sont les mêmes lorsqu'ils ne le sont tout simplement pas. Voilà l'arrondi.

En particulier, que devons-nous savoir pour pouvoir stocker des valeurs dans une certaine devise et l'imprimer?

S'il vous plaît, si vous manipulez mon argent décimal, n'utilisez pas de flottants ou de doubles.

Si vous êtes sûr que des dixièmes de centimes ne seront pas impliqués, stockez simplement des centimes. Si vous ne l'êtes pas, déterminez quelle sera la plus petite unité de cette monnaie et utilisez-la. Si vous ne le pouvez pas, utilisez quelque chose comme BigDecimal .

Ma valeur nette ira probablement toujours très bien dans un entier 64 bits, mais des choses comme BigInteger fonctionnent bien pour des projets plus grands que cela. Ils sont juste plus lents que les types natifs.

Trouver comment le stocker n'est que la moitié du problème. N'oubliez pas que vous devez également pouvoir l'afficher. Un bon design séparera ces deux choses. Le vrai problème avec l'utilisation de flotteurs ici, c'est que ces deux choses sont mises ensemble.

entrez la description de l'image ici

candied_orange
la source
6
Je ne cherchais pas vraiment une autre explication de pourquoi IEEE 754 ne fonctionne pas pour de l'argent. J'ai mentionné dans le PO que cela a été bien expliqué ailleurs. De même, il y a une raison pour laquelle j'ai utilisé le terme «utilisation physique». En effet, bien que les prix du gaz, les valeurs boursières, etc. puissent avoir une précision arbitraire, l'argent physique (et le grand nombre de transactions des utilisateurs) ne dépasse pas la centaine (et divers logiciels ne veulent représenter que ce qui peut être payé avec argent physique). Une information que je me suis demandée était de savoir s'il existait des monnaies qui étaient plus décimales.
Kat
3
La "bizarrerie" de la virgule flottante ne se limite pas à la moitié seulement contre 1 / 10e de. L'aspect "virgule flottante" du nom entraîne également des résultats parfois inattendus.
whatsisname
6
Avant la décimalisation, la monnaie britannique était le livre, le shillings et le pence, (£ sd), avec 1 £ == 20s, 1s = 12d donc 1 £ = 240d donc 1d = 0,0046666666666 .. donc ne pouvait même pas être représenté comme une décimale. Avant de rejoindre l'euro, la lire italienne a dépassé 2346,4 (en 2001) par rapport au dollar américain, de sorte que les salaires et les prix des logements atteignent parfois la limite supérieure des flotteurs à simple précision et que les chiffres économiques atteignent les niveaux supérieurs de doubles.
Steve Barnes
2
Je dirais simplement que la devise est une chose et que le prix en est une autre. Vous pouvez avoir plus de 0,01 $ de précision dans les prix, mais vous ne pouvez pas payer efficacement avec cette précision.
Machado
1
Aussi, des photos de famille . Pft. :)
Machado
10

Je peux trouver de nombreuses questions sur les bibliothèques à utiliser pour représenter des montants dans une certaine devise.

Les "bibliothèques" ne sont pas nécessaires sauf si la bibliothèque standard de votre langue manque dans certains types de données, comme je l'expliquerai.

Il y a sûrement beaucoup plus à savoir sur la monnaie dans l'utilisation du monde réel. Je suis particulièrement intéressé par ce que vous devez savoir pour le représenter dans un usage physique (par exemple, avec le dollar, vous n'avez jamais une précision inférieure à 0,01 $, permettant la représentation sous la forme d'un nombre entier de cents).

Tout simplement, vous avez besoin d' une décimale à virgule fixe , et non d' une décimale à virgule flottante . Par exemple, la classe BigDecimal de Java peut être utilisée pour stocker un montant en devise. D'autres langages modernes ont des types similaires intégrés, y compris C # et Python . Les implémentations varient, mais elles stockent généralement un nombre sous forme d'entier, l'emplacement décimal étant un membre de données distinct. Cela donne une précision exacte, même lors de l'exécution d'une arithmétique qui donnerait des restes impairs (par exemple, 0.0000001) avec des nombres à virgule flottante IEEE.

En particulier, que devons-nous savoir pour pouvoir stocker des valeurs dans une certaine devise et l'imprimer?

Il y a quelques points importants.

  1. Utilisez un type décimal réel plutôt qu'une virgule flottante.

  2. Sachez qu'un montant en devise a deux composantes: une valeur (5,63) et un code ou type de devise (USD, CAD, GBP, EUR, etc.). Parfois, vous pouvez ignorer le code de la devise, d'autres fois, il est vital. Et si vous travaillez sur un système financier ou de vente au détail / e-commerce qui autorise plusieurs devises? Que se passe-t-il si vous essayez de prendre de l'argent d'un client en CAD, mais qu'il souhaite payer avec MXN? Vous avez besoin d'un type "argent" avec un code de devise et un montant de devise pour pouvoir mélanger ces valeurs (également le taux de change, mais je ne veux pas aller trop loin sur une tangente). Dans le même temps, mon logiciel de finances personnelles n'a jamais à s'en soucier car tout est en USD (il peut mélanger les devises, mais je n'en ai jamais besoin).

  3. Alors qu'une devise peut avoir la plus petite unité physique dans la pratique (CAD et USD ont des cents, JPY est juste ... Yen), il est possible de devenir plus petit. La réponse de CandiedOrange indique les prix du carburant en dixièmes de cent. Mes impôts fonciers sont évalués en millièmes de dollar, ou en dixièmes de cent (1/1000 de dollar américain). Ne vous limitez pas à 0,01 $. Bien que vous puissiez afficher ces valeurs la plupart du temps, vos types doivent autoriser des valeurs plus petites (les types décimaux référencés ci-dessus le font).

  4. Les calculs intermédiaires doivent certainement permettre plus de précision qu'un seul centime. J'ai travaillé sur des systèmes de vente au détail / e-commerce où les valeurs internes ont été arrondies à 0,0000000 $ en interne. La précision infinie n'est généralement pas prise en charge par les types décimaux (ou SQL), il doit donc y avoir une certaine limite. Par exemple, diviser 1/3 en utilisant BigDecimal de Java lèvera une exception sans RoundingMode ou MathContext spécifié car la valeur ne peut pas être représentée exactement.

    Quoi qu'il en soit, cela est essentiel dans certains cas. Supposons que vous ayez un utilisateur avec six articles dans son panier et qu'il passe à la caisse. Votre système doit calculer la taxe et le fait par article, car les articles peuvent être taxés différemment. Si vous arrondissez les taxes pour chaque article, vous pouvez obtenir des erreurs d'arrondi de centime au niveau de la transaction / du panier. Une approche pour résoudre ce problème pourrait être de stocker les taxes à plus de décimales par article, d'obtenir le total pour la transaction entière, et de revenir en arrière et d'arrondir chaque article pour que la taxe totale soit correcte (peut-être qu'un article arrondit vers le haut, un autre vers le bas).

La chose importante à réaliser de tout cela est que quelque chose d'aussi important que l'arrondissement de pièces de un cent peut être très important pour les bonnes personnes (par exemple certains de mes anciens clients qui ont dû payer les taxes de vente du gouvernement au nom de leurs clients). Cependant, ce sont tous des problèmes résolus. Gardez à l'esprit les points ci-dessus et faites vous-même des expériences, et vous apprendrez en faisant.


la source
Sur 2, je pense que le taux de change est suffisant pour obtenir au moins son propre nombre. Vous devez tenir compte du fait que les taux changent au fil du temps, ce qui a deux façons différentes de gérer.
Izkata
2
+1. De plus, les devises changent avec le temps. Au Brésil, nous avons eu à plusieurs reprises des changements de devises au cours des années 80 et 90, réduisant les zéros et renommant la monnaie lorsque cela était nécessaire en raison d'une inflation incontrôlée. Cela signifie que chaque aspect d'une monnaie peut changer au fil du temps.
Machado
@Izkata certainement, j'ai travaillé sur des systèmes qui changent de devises. Je voulais aborder le sujet car il est pertinent. Cependant, ce n'est pas l'objet de la question et je pourrais probablement taper une autre réponse tant que celle-ci sur le thème de l'échange de devises.
«Les bibliothèques ne sont pas nécessaires, sauf si la bibliothèque standard de votre langue manque dans certains types de données, comme je l'expliquerai. Les devises peuvent avoir des moitiés, des quarts, des dixièmes, des huitièmes, etc., ce qui signifie que nous avons au moins besoin d'une représentation décimale pour elles. Mais sommes-nous absolument certains qu'il n'y a pas de devises avec un 1/3 de la pièce entière?
Daniel McLaury
4

Un endroit où de nombreux développeurs sont confrontés à une seule représentation de données pour n'importe quelle devise est celui des achats intégrés pour les applications iOS. Votre client peut être connecté à un magasin dans presque tous les pays du monde. Et dans cette situation, vous recevrez un prix d'achat composé d'un numéro à double précision et d'un code de devise.

Vous devez être conscient que les chiffres pourraient être importants. Il y a des devises où l'équivalent de dix dollars par exemple est supérieur à 100 000 unités. Et nous avons de la chance qu'il n'y ait pas de devises comme le dollar zimbabwéen en ce moment, où vous pourriez avoir des centaines de milliards de billets de banque!

Pour afficher les devises, vous aurez besoin d'une bibliothèque - vous n'avez aucune chance de tout régler vous-même. L'affichage dépend de deux choses: le code de devise et les paramètres régionaux de l'utilisateur. Pensez à la façon dont les dollars américains et les dollars canadiens seraient affichés avec les paramètres régionaux américains et canadiens: aux États-Unis, vous avez $ vs CAN $, et au Canada, vous avez $ US vs $. J'espère que c'est intégré dans le système d'exploitation, ou vous avez une bonne bibliothèque.

Pour les calculs, tout calcul se terminera par une étape d'arrondi. Vous devrez découvrir comment vous devez effectuer cet arrondissement légalement . Ce n'est pas un problème de programmation, c'est un problème juridique. Par exemple, si vous calculez la TVA au Royaume-Uni, vous devez calculer la taxe par article ou par ligne d'article, et l'arrondir à quelques centimes. Ce que vous arrondissez dépend de la devise. Mais les règles dépendent évidemment du pays. Vous ne pouvez pas vous attendre à ce qu'un calcul juridiquement correct au Royaume-Uni soit juridiquement correct au Japon et vice versa.

gnasher729
la source
0

En particulier, que devons-nous savoir pour pouvoir stocker des valeurs dans une certaine devise et les imprimer?

  • "pour stocker des valeurs" : un type de données numérique à virgule fixe et un type monétaire. Je pense que ce sont assez évidents. Le stockage sur un nombre à virgule non fixe peut impliquer un arrondi et modifier la valeur. Le calcul des devises serait un problème différent.
  • "pour les imprimer" : paramètres régionaux de l'utilisateur. Si vous êtes chanceux, vous obtenez la bibliothèque standard pour tout faire, par exemple le symbole monétaire, le séparateur de milliers, le séparateur décimal, la précision, etc.

Quelques exemples de problèmes liés aux paramètres régionaux:

  • 100 USD dans les paramètres régionaux américains devraient être de 100,00 $, dans les autres paramètres régionaux avec un point comme séparateur décimal, 100,00 $ US.
  • Certains pays utilisent une myriade au lieu de mille pour regrouper les numéros, par exemple au lieu de 10 000,00, ce ne serait que 1 000,00
  • Pour des raisons pratiques, les gens n'impriment pas tous les chiffres pour les petites devises, par exemple au lieu de 1 000 000 000,00 $, ils veulent juste que vous imprimiez 1 milliard de dollars.

Un autre problème potentiel est que même si vous stockez correctement la devise en nombre à virgule fixe, il peut être nécessaire de la convertir en nombre à virgule flottante car la bibliothèque utilisée pour l'impression prend uniquement en charge les virgules flottantes.

imel96
la source