Dans le monde trépidant de la finance, la précision est primordiale. Il est toujours préférable d'éviter une perte d'un million de dollars à cause d'une erreur d'arrondi/précision.
Le point de départ de cet article est la prise de conscience que l'argent n'est pas un nombre de base moyen que vous pouvez utiliser pour compter les pommes dans votre panier. Qu'obtient-on lorsque l'on essaie de multiplier 10€ par 10$ ? Difficile, hein… Avez-vous déjà trouvé un montant miraculeux de 1,546 $ dans la poche d’une vieille veste ? Ouais, je sais, ce n'est pas vraiment possible non plus. Ces exemples idiots sont là pour illustrer le fait que l’argent a ses propres règles particulières et ne peut pas être modélisé uniquement par un simple nombre. Je vous rassure, je ne suis pas le premier à m'en rendre compte (et peut-être que vous l'avez réalisé bien avant moi). En 2002, le programmeur Martin Fowler a proposé dans les Patterns of Enterprise Application Architecture une manière de représenter l'argent, avec des attributs et des règles d'opérandes spécifiques. Pour lui, les deux attributs minimaux viables nécessaires pour un type de données money étaient :
amount currency
Cette représentation vraiment basique sera notre point de départ pour construire un modèle monétaire simple mais robuste.
Une somme d'argent est bien un nombre particulier : il a une précision fixe (encore une fois, vous ne pouvez pas avoir 4,376$ en poche). Vous devez choisir une manière de le représenter qui vous aide à respecter cette contrainte.
Alerte spoiler, ce n'est certainement pas une bonne idée si vous ne voulez pas voir quelques centimes (si ce n'est pas des dollars) disparaître dans le monde sombre de la représentation des nombres à virgule flottante.
Si vous avez une certaine expérience du codage en JavaScript, vous savez que même le calcul le plus simple peut entraîner une erreur de précision à laquelle vous ne vous attendriez pas au début. L’exemple le plus évident et le plus connu pour mettre en évidence ce phénomène est :
0.1 0.2 !== 0.3 // true 0.1 0.2 // 0.30000000000000004
Si cet exemple ne vous convainc pas entièrement, je vous conseille de jeter un œil à cet article qui approfondit un peu plus tous les résultats de calcul problématiques que vous pouvez rencontrer en travaillant avec le type de nombre natif JavaScript…
Ce léger écart dans les résultats peut vous sembler inoffensif (avec une ampleur d'environ ~ 10^-16), mais dans une application financière critique, une telle erreur peut se produire rapidement. Envisagez de transférer des fonds entre des milliers de comptes, où chaque transaction implique des calculs similaires. Les légères inexactitudes s’additionnent et avant que vous vous en rendiez compte, vos états financiers sont erronés de plusieurs milliers de dollars. Et honnêtement, nous pouvons tous convenir qu’en matière d’argent, l’erreur n’est pas autorisée : tant sur le plan juridique que pour construire une relation de confiance avec vos clients.
La première question que je me suis posée lorsque j'ai rencontré le problème dans l'un de mes projets est pourquoi ? J'ai découvert que la source du problème n'est pas JavaScript et que ces imprécisions affectent également d'autres langages de programmation modernes (Java, C, Python, …).
// In C #includeint main() { double a = 0.1; double b = 0.2; double sum = a b; if (sum == 0.3) { printf("Equal\n"); } else { printf("Not Equal\n"); // This block is executed } return 0; } // > Not equal
// In Java public class doublePrecision { public static void main(String[] args) { double total = 0; total = 5.6; total = 5.8; System.out.println(total); } } // > 11.399999999999
En fait, la cause première réside dans la norme utilisée par ces langages pour représenter les nombres à virgule flottante : le format à virgule flottante double (ou simple) précision, spécifié par la norme IEEE 754.
En Javascript, le nombre de type natif correspond à des nombres à virgule flottante double précision, ce qui signifie qu'un nombre est codé sur 64 bits et divisé en trois parties :
Ensuite, vous devez utiliser la formule suivante pour convertir votre représentation binaire en valeur décimale :
Un exemple de représentation de nombres à virgule flottante double précision, une approximation de 1/3 :
0 01111111101 0101010101010101010101010101010101010101010101010101 = (-1)^0 x (1 2^-2 2^-4 2^-6 ... 2^-50 2^-52) x 2^(1021-1023) = 0.333333333333333314829616256247390992939472198486328125 ~ 1/3
Ce format nous permet de représenter une vaste gamme de valeurs, mais il ne peut pas représenter tous les nombres possibles avec une précision absolue (juste entre 0 et 1, vous pouvez trouver une infinité de nombres…). De nombreux nombres ne peuvent pas être représentés exactement sous forme binaire. Pour revenir au premier exemple, c'est le problème avec 0,1 et 0,2. La représentation flottante à double virgule nous donne une approximation de ces valeurs, donc lorsque vous ajoutez ces deux représentations imprécises, le résultat n'est pas non plus exact.
Maintenant que vous êtes pleinement convaincu que gérer des sommes d'argent avec un type de numéro JavaScript natif est une mauvaise idée (du moins j'espère que vous commencez à avoir des doutes à ce sujet), la question à 1 milliard de dollars est comment devez-vous procéder ? Une solution pourrait consister à utiliser certains des puissants packages arithmétiques à précision fixe disponibles en JavaScript. Par exemple Decimal.js (qui est utilisé par le populaire ORM Prisma pour représenter son type de données Decimal) ou Big.js.
Ces packages vous fournissent des types de données spéciaux qui vous permettent d'effectuer des calculs en vous débarrassant des erreurs de précision que nous avons expliquées ci-dessus.
// Example using Decimal.js const Decimal = require('decimal.js'); const a = new Decimal('0.1'); const b = new Decimal('0.2'); const result = a.plus(b); console.log(result.toString()); // Output: '0.3'
Cette approche vous offre un autre avantage, elle étend considérablement la valeur maximale pouvant être représentée, ce qui peut s'avérer très pratique lorsque vous avez affaire à des crypto-monnaies par exemple.
Même s'il est vraiment robuste, ce n'est pas celui que je préfère choisir d'implémenter dans mes applications web. Je trouve plus facile et plus clair d'appliquer la stratégie Stripe pour traiter uniquement des valeurs entières.
Nous, chez Theodo Fintech, valorisons le pragmatisme ! Nous aimons nous inspirer des entreprises les plus performantes du secteur. Stripe, la célèbre société milliardaire spécialisée dans les services de paiement, a fait le choix de gérer des sommes d'argent sans nombres flottants mais avec des nombres entiers. Pour ce faire, ils utilisent la plus petite unité de la monnaie pour représenter un montant monétaire.
// 10 USD are represented by { "amount": 1000, "currency": "USD" }
Je suppose que beaucoup d'entre vous le savent déjà : toutes les monnaies n'ont pas la plus petite unité de même grandeur. La plupart d'entre elles sont des monnaies « à deux décimales » (EUR, USD, GBP), ce qui signifie que leur plus petite unité est 1/100ème de la monnaie. Cependant, certaines sont des monnaies « à trois décimales » (KWD) ou même des monnaies « à zéro décimale » (JPY). (Vous pouvez trouver plus d'informations à ce sujet en suivant la norme ISO4217). Pour gérer ces disparités, vous devez intégrer à la représentation de vos données monétaires le facteur multiplicatif pour convertir un montant représenté dans la plus petite unité dans la devise correspondante.
Je suppose que vous l'avez déjà compris, vous pouvez soit travailler avec des nombres natifs, des packages tiers à précision arbitraire ou des nombres entiers, les calculs peuvent (et vous mèneront) à des résultats à virgule flottante que vous devrez arrondir à votre argent. précision finie. Comme un exemple rapide n'est jamais trop, disons que vous manipulez des valeurs entières et contractez un prêt de 16k$ avec un taux d'intérêt bien précis de 8,5413% (aïe…). Vous devez ensuite rembourser 16 000 $ plus un montant supplémentaire de
1600000 * 0.085413 // amount in cents //Output in cents: 136660.8
L'essentiel est de gérer correctement le processus d'arrondi des montants après les calculs. La plupart du temps, vous devez choisir entre trois types d'arrondi différents :
En matière d'arrondis, il n'y a pas vraiment de "sauce magique" : il faut arbitrer en fonction de sa situation. Je vous recommande de toujours vérifier la législation lorsque vous avez affaire à une nouvelle devise et à un nouveau cas d'utilisation de l'arrondi (conversion, partage d'argent, taux d'intérêt des crédits, …). Vous feriez mieux de suivre les réglementations immédiatement pour éviter de nouveaux problèmes. Par exemple, en ce qui concerne les taux de conversion, la plupart des devises ont déterminé des règles concernant la précision nécessaire et les règles d'arrondi (vous pouvez consulter les règles du taux de conversion de l'EUR ici).
Cet article n'est pas exhaustif sur toutes les possibilités existantes pour gérer les sommes d'argent en JavaScript et n'est pas non plus destiné à vous fournir un modèle de données monétaires complet/parfait. J'ai essayé de vous donner suffisamment d'indices et de lignes directrices pour mettre en place une représentation qui s'est avérée cohérente, résiliente et qui a été choisie par les grands acteurs de l'industrie fintech. J'espère que vous pourrez effectuer des calculs de montants d'argent dans vos futurs projets sans oublier aucun centime en poche (sinon n'oubliez pas de jeter un oeil dans votre ancienne veste) !
Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.
Copyright© 2022 湘ICP备2022001581号-3