La gestion de la précision numérique est une pierre angulaire de la comptabilité en partie double. En tenue de livres numérique, surtout lorsqu'il s'agit de plusieurs devises, de cours d'actions et de fractions d'actions, de petits écarts d'arrondi peuvent rapidement mener à des erreurs d'équilibrage frustrantes. Beancount offre un système sophistiqué mais intuitif pour gérer la précision et définir des tolérances acceptables. Ce guide vous expliquera son fonctionnement. ⚙️
Chaque nombre de cette page a été vérifié avec Beancount 3.2.3, y compris les limites : chaque exemple précise quel résidu est accepté et lequel est d'un chiffre trop loin.
Concepts fondamentaux de la précision
L'objectif principal de Beancount est de garantir que chaque transaction s'équilibre à zéro. Cependant, les calculs impliquant des prix ou des coûts produisent souvent des résultats avec plus de décimales qu'il n'est pratique d'en enregistrer. Le système de tolérance permet de petits déséquilibres acceptables.
Inférence automatique de la tolérance
Par défaut, Beancount infère automatiquement la tolérance requise pour chaque transaction. Cette inférence est effectuée individuellement pour chaque transaction et calculée séparément pour chaque devise concernée.
La règle est une multiplication : la tolérance pour une devise est le plus petit chiffre observé dans les montants des postings de cette devise, multiplié par l'option tolerance_multiplier, dont la valeur par défaut est 0.5. Avec cette valeur par défaut, la tolérance est la moitié du dernier chiffre significatif.
Par exemple, considérons cet achat :
2013-04-03 * "Buy Fund"
Assets:Fund 10.22626 FUND {37.61 USD}
Assets:Cash -384.61 USDBeancount infère les tolérances comme suit :
- Pour la devise
FUND, le nombre10.22626a 5 décimales. La tolérance est la moitié du dernier chiffre, soit $0.00001 \div 2 = 0.000005$FUND. - Pour la devise
USD, le nombre-384.61a 2 décimales. La tolérance est la moitié du dernier chiffre, soit $0.01 \div 2 = 0.005$USD.
La contrepartie en espèces est ce contre quoi la tolérance est mesurée : 10.22626 × 37.61 donne 384.6096386, donc cette transaction est à 0.0003614 USD de zéro et se charge. Arrondissez la contrepartie en espèces à -384.60 et l'écart devient 0.0096386 USD, dépassant la tolérance de 0.005, et Beancount signale Transaction does not balance.
Règles de poids des transactions
Lors de la vérification de l'équilibre d'une transaction, Beancount calcule le « poids » de chaque posting. Les règles pour ce calcul sont :
- Montant simple : Si un posting n'a qu'un montant (par exemple
Assets:Cash -100.00 USD), son poids est ce montant exact. - Posting avec prix : Si un posting a un prix unitaire (par exemple
10 FUND @ 38.46 USD), son poids estamount × price. - Coût unitaire : Les accolades simples contiennent le coût d'une unité, donc
10 FUND {384.61 USD}pèse10 × 384.61 = 3,846.10 USD, pas384.61 USD. - Coût total : Les doubles accolades contiennent le coût de l'ensemble du posting, donc
10 FUND {{384.61 USD}}pèse384.61 USD. Beancount le convertit en coût unitaire de38.461 USDlors du stockage du lot. - Coût et prix : Si un posting a à la fois un coût et un prix unitaire (par exemple
10 FUND {384.61 USD} @ 400.00 USD), seul le coût est utilisé pour l'équilibrage. Le prix est enregistré pour les rapports, pas pour les calculs.
Les règles 3 et 4 sont celles qui coûtent un après-midi aux gens, alors les voici côte à côte dans un fichier qui se charge :
1970-01-01 open Assets:Fund
1970-01-01 open Assets:Cash
; Per-unit cost: ten units at 384.61 each, so 3,846.10 USD leaves the
; cash account.
2013-04-03 * "Broker" "Buy at a per-unit cost"
Assets:Fund 10 FUND {384.61 USD}
Assets:Cash -3846.10 USD
; Total cost: the braces double and 384.61 USD is the entire purchase.
; The lot is stored at 38.461 USD per unit.
2013-04-04 * "Broker" "Buy at a total cost"
Assets:Fund 10 FUND {{384.61 USD}}
Assets:Cash -384.61 USDLe compte se termine avec 20 FUND en deux lots, 4,230.71 USD de coût de base entre eux.
Règles d'inférence de précision
Le système d'inférence automatique suit quelques règles spécifiques :
- Format des nombres
- Les montants entiers (par exemple
10 USD) ne contribuent pas à l'inférence de précision. - Une décimale est le niveau le plus grossier qu'un montant peut impliquer :
0.1 × 0.5 = 0.05unités. Au-delà, vous devez utilisertolerance_multiplierou une valeur par défaut par devise, comme expliqué ci-dessous. - Les coûts et les prix (par exemple
{37.61 USD}) sont exclus de l'inférence de tolérance par défaut. Seuls les montants principaux des postings sont utilisés. - Si des postings pour la même devise ont des précisions différentes (par exemple
-10.10 USDet5.123 USD), Beancount utilise la tolérance la plus grossière (la plus grande). Dans ce cas, elle serait basée sur-10.10 USD, donnant une tolérance de $0.005$USD.
-
Gestion par défaut Vous pouvez définir une tolérance par défaut globale ou spécifique à une devise si une transaction ne comporte aucun nombre avec des décimales à partir duquel l'inférer.
; Sets a default tolerance for all currencies without explicit rules option "inferred_tolerance_default" "*:0.001" ; Sets a specific default tolerance for USD option "inferred_tolerance_default" "USD:0.003" -
Multiplicateur de tolérance L'option est
tolerance_multiplier, et c'est la fraction du plus petit chiffre qui compte comme tolérable — pas un pourcentage ajouté en plus. Sa valeur par défaut est0.5, donc définir1.2ne relâche pas les vérifications de 20 % : cela rend chaque tolérance inférée 2,4 fois plus grande que celle par défaut.option "tolerance_multiplier" "1.2" 1970-01-01 open Assets:Cash 1970-01-01 open Expenses:Fees ; The coarsest amount has two decimals, so the tolerance is ; 1.2 x 0.01 = 0.012 USD, and this residual of exactly 0.012 passes. ; At the default 0.5 the tolerance would be 0.005 and this would fail. 2024-05-01 * "Bank" "Wire fee" Expenses:Fees 100.00 USD Assets:Cash -99.988 USDL'ancien nom
inferred_tolerance_multiplierdéfinit la même valeur mais signaleRenamed to 'tolerance_multiplier'.comme erreur de chargement. -
Inférence basée sur les coûts Bien que les coûts soient normalement ignorés pour l'inférence de tolérance, vous pouvez demander à Beancount de les utiliser. Cela est utile lorsque le montant final (par exemple un retrait en espèces) est le nombre le plus précis d'une transaction.
option "infer_tolerance_from_cost" "TRUE"
Voici la valeur par défaut simple, sans aucune option, à sa limite exacte :
1970-01-01 open Assets:Cash
1970-01-01 open Expenses:Fees
; Two decimals on the coarsest amount, so the tolerance is
; 0.5 x 0.01 = 0.005 USD. This residual is exactly 0.005 and passes;
; -99.994 would be 0.006 and would fail.
2024-05-01 * "Bank" "Wire fee"
Expenses:Fees 100.00 USD
Assets:Cash -99.995 USDAssertions de solde
Les assertions de solde (balance) servent à vérifier que le solde de votre compte correspond à une valeur connue à une date spécifique. Elles ont également une tolérance associée.
Format de base
La tolérance d'une assertion balance est déduite du nombre de décimales du montant, mais elle est deux fois plus généreuse que celle utilisée dans une transaction : tolerance_multiplier × 2 × the smallest digit. Avec le multiplicateur par défaut, cela représente exactement une unité de la dernière décimale que vous avez écrite.
; Asserts the balance is 4.271 RGAGX with a tolerance of +/-0.001
2015-05-08 balance Assets:Fund 4.271 RGAGX
; Asserts the balance is 4.27 RGAGX with a tolerance of +/-0.01
2015-05-08 balance Assets:Fund 4.27 RGAGXLa comparaison est inclusive : une différence exactement égale à la tolérance passe toujours. Pour le deuxième exemple, tout solde entre $4.26$ et $4.28$ passe la vérification, et 4.2801 échoue avec Balance failed for 'Assets:Fund': expected 4.27 RGAGX != accumulated 4.2801 RGAGX (0.0101 too much).
1970-01-01 open Assets:Fund
1970-01-01 open Equity:Opening-Balances
1970-01-02 * "Broker" "Opening position"
Assets:Fund 4.28 RGAGX
Equity:Opening-Balances -4.28 RGAGX
; 4.28 is 0.01 away from the asserted 4.27, which is the whole tolerance.
2015-05-08 balance Assets:Fund 4.27 RGAGXTolérances explicites
Si la tolérance inférée ne convient pas, vous pouvez en spécifier une explicitement en utilisant le caractère tilde (~). C'est la seule syntaxe de tolérance explicite de Beancount, et elle fonctionne uniquement sur les directives balance — un tilde dans un posting de transaction est une erreur de syntaxe.
1970-01-01 open Assets:Fund
1970-01-01 open Equity:Opening-Balances
1970-01-02 * "Broker" "Opening position"
Assets:Fund 4.281 RGAGX
Equity:Opening-Balances -4.281 RGAGX
; Asserts the balance is 4.271 RGAGX with a custom tolerance of
; +/-0.01 RGAGX, so anything from 4.261 to 4.281 passes.
2015-05-08 balance Assets:Fund 4.271 ~ 0.01 RGAGXPoussez la participation à 4.2811 et la même assertion échoue de 0.0101.
Gestion des arrondis
Les petits résidus issus des calculs de coûts et de prix sont normaux. Ce que Beancount en fait est plus limité qu'il n'y paraît.
Suivi des erreurs d'arrondi
L'option account_rounding nomme un compte destiné à absorber les résidus. Elle prend un nom de compte complet et est stockée exactement telle que vous l'écrivez — aucun préfixe de capitaux propres n'est ajouté, contrairement aux options de comptes de capitaux propres.
option "account_rounding" "Equity:Rounding"
1970-01-01 open Assets:Invest
1970-01-01 open Assets:Cash
1970-01-01 open Equity:Rounding
; 1.245 x 43.23 = 53.82135, so this is 0.00135 USD short of balancing.
2013-02-23 * "Broker" "Purchase"
Assets:Invest 1.245 RGAGX {43.23 USD}
Assets:Cash -53.82 USDDans cette transaction, . La transaction est déséquilibrée de $-0.00135$ USD, ce qui est dans la tolérance inférée de 0.005 USD, donc elle se charge.
Sur Beancount 3.2.3, rien n'est posté sur Equity:Rounding. L'option est analysée et stockée, mais aucune étape du chargeur n'insère le posting de résidu, donc le compte reste à zéro et un résidu hors tolérance reste une erreur plutôt que d'être absorbé :
option "account_rounding" "Equity:Rounding"
1970-01-01 open Assets:Invest
1970-01-01 open Assets:Cash
1970-01-01 open Equity:Rounding
; 0.10135 USD out, far past the 0.005 tolerance. Setting
; account_rounding does not rescue it:
; Transaction does not balance: (0.10135 USD)
2013-02-23 * "Broker" "Purchase"
Assets:Invest 1.245 RGAGX {43.23 USD}
Assets:Cash -53.72 USDConsidérez donc account_rounding comme inerte sur cette version. Si vous voulez qu'un résidu soit enregistré plutôt que toléré, écrivez vous-même le troisième posting :
1970-01-01 open Assets:Invest
1970-01-01 open Assets:Cash
1970-01-01 open Equity:Rounding
2013-02-23 * "Broker" "Purchase"
Assets:Invest 1.245 RGAGX {43.23 USD}
Assets:Cash -53.82 USD
Equity:Rounding -0.00135 USDCette version s'équilibre exactement à zéro, et la poussière est visible dans un compte sur lequel vous pouvez faire des rapports.
Précision numérique inférée
Beancount n'arrondit pas les nombres que vous écrivez. Il n'y a pas d'option default_tolerance — elle n'existe pas et fait échouer le chargement avec Invalid option: 'default_tolerance' — et aucun paramètre ne quantifie les montants stockés.
-
Le stockage est toujours exact. Écrivez
53.82135 USDet le grand livre contient53.82135 USD, quels que soient vos paramètres de tolérance. La tolérance décide si une transaction est acceptée ; elle ne modifie jamais un nombre. -
L'affichage est un paramètre distinct.
display_precisionfixe le nombre de chiffres fractionnaires avec lequel une devise est rendue, et ne change rien à la valeur stockée ni à la vérification du solde.option "display_precision" "USD:0.01" 1970-01-01 open Assets:Cash 1970-01-01 open Income:Interest ; Rendered as 53.82 USD, stored as 53.82135 USD. 2024-06-30 * "Bank" "Interest" Assets:Cash 53.82135 USD Income:Interest -53.82135 USD -
L'arrondi est un posting à écrire. Si vous voulez sortir le résidu du calcul, arrondissez le montant dans la source et comptabilisez la différence explicitement, comme dans l'exemple à trois postings ci-dessus.
Détails d'implémentation
Quelques points techniques clarifient comment Beancount atteint cette fiabilité.
-
Représentation des nombres : Beancount utilise le module
decimalde Python, pas les nombres à virgule flottante. Le contexte par défaut porte 28 chiffres significatifs — chiffres au total, pas après la virgule — ce qui évite les erreurs de représentation binaire courantes avec les flottants. -
Classe DisplayContext : Cette classe interne gère tout le formatage des nombres à des fins d'affichage. Elle déduit la précision de chaque devise à partir des nombres de votre fichier, sauf si
display_precisionla fixe, et peut formater la sortie avec des colonnes alignées et des virgules. -
Précision vs. Tolérance : Il est crucial de distinguer ces deux concepts :
- La précision concerne le format d'affichage d'un nombre (combien de décimales sont affichées).
- La tolérance est l'allocation pour déséquilibre utilisée lors des vérifications.
Meilleures pratiques ✨
Voici quelques recommandations pratiques pour gérer la précision dans votre grand livre.
Configuration initiale
Pour la plupart des nouveaux grands livres, voici une configuration de départ robuste :
; A floor for currencies that have no decimals to infer from
option "inferred_tolerance_default" "*:0.005"
; Leave the multiplier at its 0.5 default unless a real institution
; forces your hand; 1.2 would mean 2.4x the usual tolerance.
option "tolerance_multiplier" "0.5"Conseils de dépannage
Si vous rencontrez des erreurs d'équilibrage :
- Ajoutez des décimales au montant d'un posting pour créer une inférence de tolérance locale plus stricte et plus précise.
- Utilisez des tolérances explicites (
~) sur les assertionsbalancequi échouent en raison d'écarts prévisibles. - Comptabilisez le résidu sur un compte dédié avec un vrai troisième posting, afin de pouvoir faire des rapports sur sa fréquence.
- Envisagez de définir des valeurs par défaut spécifiques à une devise si vous traitez fréquemment des devises avec des conventions différentes (par exemple, le JPY n'a pas de décimales).
Stratégie de migration
Lorsque vous appliquez ces concepts à un grand livre existant et désordonné :
- Commencez avec une tolérance globale généreuse (par exemple
*:0.05) et untolerance_multiplierplus élevé pour faire valider le fichier. - Resserrez progressivement les tolérances et corrigez les erreurs qui apparaissent.
- Ajoutez des chiffres explicites aux montants des transactions problématiques pour laisser l'inférence faire son travail.
- Surveillez le solde du compte d'arrondi. Un solde important ou en croissance rapide peut signaler un problème systémique nécessitant une investigation.