Nous avons récemment mis en œuvre un système qui doit gérer les valeurs dans plusieurs devises et effectuer une conversion entre elles, et nous avons résolu certaines choses à la dure.
NE JAMAIS UTILISER DE NUMÉROS DE POINTS FLOTTANTS POUR DE L'ARGENT
L'arithmétique en virgule flottante introduit des inexactitudes qui peuvent ne pas être remarquées tant qu'ils n'ont pas foiré quelque chose. Toutes les valeurs doivent être stockées sous forme d'entiers ou de types décimaux fixes, et si vous choisissez d'utiliser un type décimal fixe, assurez-vous de comprendre exactement ce que fait ce type sous le capot (c'est-à-dire, utilise-t-il en interne un entier ou une virgule flottante type).
Lorsque vous devez effectuer des calculs ou des conversions:
- Convertir les valeurs en virgule flottante
- Calculer une nouvelle valeur
- Arrondissez le nombre et reconvertissez-le en entier
Lors de la conversion d'un nombre à virgule flottante en un entier à l'étape 3, ne vous contentez pas de le convertir - utilisez une fonction mathématique pour l'arrondir en premier. Ce sera généralement le cas round
, bien que dans des cas particuliers, cela puisse être floor
ou ceil
. Connaissez la différence et choisissez avec soin.
Stocker le type d'un nombre à côté de la valeur
Cela peut ne pas être aussi important pour vous si vous ne manipulez qu'une seule devise, mais c'était important pour nous dans le traitement de plusieurs devises. Nous avons utilisé le code à 3 caractères pour une devise, telle que USD, GBP, JPY, EUR, etc.
Selon la situation, il peut également être utile de stocker:
- Si le numéro est avant ou après impôt (et quel était le taux d'imposition)
- Si le nombre est le résultat d'une conversion (et de quoi il a été converti)
Connaissez les limites de précision des nombres avec lesquels vous traitez
Pour les valeurs réelles, vous voulez être aussi précis que la plus petite unité de la devise. Cela signifie que vous n'avez pas de valeurs inférieures à un cent, un centime, un yen, un fen, etc. Ne stockez pas les valeurs avec une précision plus élevée que cela sans raison.
En interne, vous pouvez choisir de traiter des valeurs plus petites, auquel cas il s'agit d'un type de valeur de devise différent . Assurez-vous que votre code sait lequel est lequel et ne les confond pas. Évitez d'utiliser des valeurs à virgule flottante même ici.
En ajoutant toutes ces règles ensemble, nous avons décidé des règles suivantes. Dans le code en cours d'exécution, les devises sont stockées en utilisant un entier pour la plus petite unité.
class Currency {
String code; // eg "USD"
int value; // eg 2500
boolean converted;
}
class Price {
Currency grossValue;
Currency netValue;
Tax taxRate;
}
Dans la base de données, les valeurs sont stockées sous forme de chaîne au format suivant:
USD:2500
Cela stocke la valeur de 25,00 $. Nous avons pu le faire uniquement parce que le code qui traite des devises n'a pas besoin d'être dans la couche de base de données elle-même, de sorte que toutes les valeurs peuvent être converties en mémoire en premier. D'autres situations se prêteront sans doute à d'autres solutions.
Et au cas où je ne l'ai pas précisé plus tôt, n'utilisez pas de flotteur!