web-dev-qa-db-fra.com

Utiliser des valeurs flottantes ou décimales pour le montant en dollars d’application comptable

Nous sommes en train de réécrire notre système de comptabilité hérité dans VB.NET et SQL Server. Nous avons fait appel à une nouvelle équipe de programmeurs .NET/SQL pour effectuer la réécriture. La plupart du système est déjà terminé avec les montants en dollars à l’aide de Floats. L’ancien langage système, dans lequel j’ai programmé, n’avait pas de flotteur, donc j’aurais probablement utilisé un nombre décimal.

Quelle est votre recommandation?

Les types de données Float ou Decimal doivent-ils être utilisés pour des montants en dollars?

Quels sont certains des avantages et des inconvénients pour l'un ou l'autre?

Un problème mentionné dans notre journal quotidien était que vous devez faire attention lorsque vous calculez un montant qui renvoie un résultat supérieur à deux décimales. Il semble que vous devrez arrondir le montant à deux décimales.

Un autre inconvénient est que tous les affichages et les montants imprimés doivent avoir une déclaration de format qui indique deux positions décimales. J'ai remarqué à quelques reprises que cela n'était pas fait et que les montants ne semblaient pas corrects. (i.e. 10.2 ou 10.2546)

Un pro est le Float n'occupe que 8 octets sur le disque alors que le Decimal prendrait 9 octets (Decimal 12,2) 

74
Gerhard Weiss

Les types de données Float ou Decimal doivent-ils être utilisés pour des montants en dollars?

La réponse est facile. Ne flotte jamais. JAMAIS !

Les flotteurs étaient conformes à IEEE 754 toujours binaire, uniquement à la nouvelle norme IEEE 754R formats décimaux définis. La plupart des parties binaires fractionnaires ne peuvent jamais être égales à la représentation décimale exacte.
Tout nombre binaire peut être écrit sous la forme m/2^n (m, n nombres entiers positifs), chaque nombre décimal sous la forme m/(2^n*5^n).
Comme les binaires n'ont pas le factor 5 premier, tous les nombres binaires peuvent être représentés exactement par des nombres décimaux, mais pas l'inverse.

0.3 = 3/(2^1 * 5^1) = 0.3

0.3 = [0.25/0.5] [0.25/0.375] [0.25/3.125] [0.2825/3.125]

          1/4         1/8         1/16          1/32

Vous vous retrouvez donc avec un nombre supérieur ou inférieur au nombre décimal donné. Toujours.

Pourquoi est-ce important? Arrondi.
Arrondir normalement signifie 0..4 en bas, 5..9 en haut. Donc importe si le résultat est Soit 0.049999999999.... ou 0.0500000000... Vous savez peut-être que cela signifie 5 centimes, mais l’ordinateur ne le sait pas et arrondit 0.4999... vers le bas (faux) et 0.5000... en haut (droite).
Étant donné que le résultat des calculs en virgule flottante contient toujours de petits termes d'erreur, la décision est pur hasard. Cela devient sans espoir si vous voulez un traitement décimal arrondi à égal avec des nombres binaires.

Pas convaincu? Vous insistez pour que tout se passe parfaitement dans votre système de compte?
Actif et passif égal? Ok, alors prenons chacun des nombres formatés donnés pour chaque entrée, les analysons et additionnez-les avec un système décimal indépendant! .__ Comparez cela avec la somme formatée.
Oups, il y a quelque chose qui ne va pas, n'est-ce pas? 

Pour ce calcul, une précision et une fidélité extrêmes étaient nécessaires (nous avons utilisé Oracle FLOAT) pour pouvoir enregistrer le "milliardième de centime" acquis.

N'aide pas contre cette erreur. Parce que tout le monde suppose automatiquement que la somme de l'ordinateur est correcte, pratiquement personne ne vérifie de façon indépendante.

105
TSK
41
Nakilon

Tout d’abord, vous devriez lire ceci Ce que chaque informaticien devrait savoir sur l’arithmétique en virgule flottante . Ensuite, vous devriez vraiment envisager d’utiliser un type de point fixe/nombre-précision arbitraire package (par exemple, Java BigNum, module décimal python), sinon vous vous retrouverez dans un monde de blessures. Déterminez ensuite si l’utilisation du type décimal SQL natif est suffisante. 

Des flotteurs/doubles existent (ed) pour exposer le rapide x87 fp qui est maintenant à peu près obsolète. Ne les utilisez pas si vous vous souciez de l'exactitude des calculs et/ou ne compensez pas complètement leurs limites.

22
Rich Schuler

Tout comme un avertissement supplémentaire, SQL Server et le framework .Net utilisent un algorithme par défaut différent pour l'arrondi. Assurez-vous de bien extraire le paramètre MidPointRounding dans Math.Round (). La structure .Net utilise l’algorithme de Bankers par défaut et SQL Server utilise l’arrondi algorithmique symétrique. Découvrez l'article Wikipedia ici

10
Darrel Miller

Demandez à vos comptables! Ils vous désapprouveront pour avoir utilisé float. Comme pour les personnes postées auparavant, utilisez float UNIQUEMENT si vous ne vous souciez pas de la précision. Bien que je sois toujours contre en ce qui concerne l’argent.

En logiciel de comptabilité n'est pas acceptable un float. Utilisez décimal avec 4 décimales.

7
Ricardo C

Les points flottants ont des nombres irrationnels inattendus.

Par exemple, vous ne pouvez pas stocker 1/3 sous forme décimale, ce serait 0.3333333333 ... (et ainsi de suite)

Les flottants sont en fait stockés sous forme de valeur binaire et d'une puissance de 2 exposants.

Donc 1.5 est stocké sous forme de 3 x 2 à -1 (ou 3/2)

L'utilisation de ces exposants en base 2 crée des nombres irrationnels impairs, par exemple:

Convertissez 1.1 en float, puis convertissez-le à nouveau, votre résultat sera comme: 1.0999999999989

En effet, la représentation binaire de 1.1 est en réalité 154811237190861 x 2 ^ -47, plus qu’un double ne peut gérer.

Plus d'informations sur ce sujet sur mon blog , mais, en gros, pour le stockage, il vaut mieux utiliser des décimales.

Sur le serveur Microsoft SQL, vous disposez du type de données money - il est généralement préférable pour le stockage financier. Il est précis à 4 décimales.

Pour les calculs, le problème est plus grave: l’imprécision est une fraction infime, mais insérez-la dans une fonction d’alimentation et elle devient rapidement importante. 

Cependant, les nombres décimaux ne sont pas très utiles pour les mathématiques - il n’existe pas de prise en charge native des pouvoirs décimaux, par exemple.

6
Keith

Un peu de contexte ici ....

Aucun système de numérotation ne peut traiter tous les nombres réels avec précision. Tous ont leurs limites, et cela inclut à la fois le nombre à virgule flottante IEEE standard et le nombre décimal signé. La virgule flottante IEEE est plus précise par bit utilisé, mais cela n'a pas d'importance ici.

Les chiffres financiers sont basés sur des siècles de pratique du papier et du stylo, avec les conventions associées. Ils sont raisonnablement précis, mais, plus important encore, ils sont reproductibles. Deux comptables travaillant avec des nombres et des taux différents devraient arriver avec le même nombre. Toute marge de divergence est susceptible de fraude.

Par conséquent, pour les calculs financiers, la bonne réponse est tout ce qui donne la même réponse qu'un CPA qui est bon en arithmétique. C'est une arithmétique décimale, pas une virgule flottante IEEE.

5
David Thornley

Utilisez le type decimal du serveur SQL.

N'utilisez pas argent ou float.

money utilise 4 décimales, est plus rapide que décimale MAIS souffre de problèmes d’arrondi évidents et de problèmes moins évidents ( voir ce problème de connexion

5
Mitch Wheat

Ce que je recommanderais, c’est d’utiliser des entiers 64 bits qui stockent le tout en centimes.

5
Joshua

Les flottants ne sont pas des représentations exactes, des problèmes de précision sont possibles, par exemple lors de l'ajout de valeurs très grandes et très petites. C'est pourquoi les types décimaux sont recommandés pour la devise, même si le problème de précision est suffisamment rare.

Pour clarifier, le type décimal 12,2 stockera ces 14 chiffres exactement, alors que le flottant ne le fera pas car il utilise une représentation binaire en interne. Par exemple, 0.01 ne peut pas être représenté exactement par un nombre à virgule flottante - la représentation la plus proche est en réalité 0.0099999998

4
Niall

Pour un système bancaire que j'ai aidé à développer, j'étais responsable de la partie "Intérêts courus" du système. Chaque jour, mon code calculait le montant des intérêts accumulés (gagnés) sur le solde de ce jour.

Pour ce calcul, une précision et une fidélité extrêmes étaient nécessaires (nous avons utilisé le logiciel FLOAT d'Oracle) afin de pouvoir enregistrer le "milliardième d'un centime" accumulé.

Lorsqu'il s'agissait de "capitaliser" les intérêts (c'est-à-dire les rembourser sur votre compte), le montant était arrondi au centime. Le type de données pour les soldes de compte était à deux décimales. (En réalité, c'était plus compliqué, car il s'agissait d'un système multi-devises pouvant fonctionner avec plusieurs décimales - mais nous avons toujours arrondi au "centime" de cette devise). Oui, il y avait des "fractions" de perte et de gain, mais lorsque les chiffres de l'ordinateur ont été actualisés (argent versé ou versé), il s'agissait toujours de valeurs en argent RÉELLES.

Cela a satisfait les comptables, les auditeurs et les testeurs.

Alors, vérifiez avec vos clients. Ils vous expliqueront leurs règles et pratiques bancaires/comptables.

4
Guy

La seule raison d'utiliser Float pour de l'argent est si vous ne vous souciez pas des réponses précises. 

4
David Singer

Une autre chose que vous devez savoir dans les systèmes de comptabilité est que personne ne devrait avoir un accès direct aux tables. Cela signifie que tout accès au système comptable doit se faire par le biais de procédures stockées. Cela évite les fraudes et pas seulement les attaques par injection SQl. Un utilisateur interne qui veut commettre une fraude ne devrait jamais avoir la possibilité de modifier directement les données dans les tables de la base de données. Ceci est un contrôle interne critique sur votre système. Voulez-vous vraiment qu'un employé mécontent se rende dans le backend de votre base de données et commence à lui envoyer des chèques? Ou cacher qu'ils ont approuvé une dépense à un vendeur non autorisé quand ils n'ont pas l'autorité d'approbation? Seules deux personnes de l’ensemble de votre entreprise devraient pouvoir accéder directement aux données de votre base de données financières, votre base de données et sa sauvegarde. Si vous avez plusieurs bases, seulement deux d'entre elles devraient avoir cet accès.

Je le mentionne parce que si vos programmeurs utilisaient float dans un système de comptabilité, ils ne connaissaient probablement pas l'idée de contrôles internes et ne les prenaient pas en compte dans leur effort de programmation.

3
HLGEM

Mieux que d'utiliser des nombres décimaux, vous utilisez simplement de vieux entiers (ou peut-être une sorte de bigint). De cette façon, vous avez toujours la plus grande précision possible, mais la précision peut être spécifiée. Par exemple, le nombre 100 pourrait signifier 1.00, qui est formaté comme ceci:

int cents = num % 100;
int dollars = (num - cents) / 100;
printf("%d.%02d", dollars, cents);

Si vous souhaitez plus de précision, vous pouvez modifier la valeur 100 en une valeur supérieure, telle que: 10 ^ n, où n représente le nombre de décimales.

3
Peter Stuifzand

Sur les 100 fractions n/100, où n est un nombre naturel tel que 0 <= n et n <100, seules quatre peuvent être représentées par des nombres à virgule flottante. Jetez un coup d’œil à la sortie de ce programme C:

#include <stdio.h>

int main()
{
    printf("Mapping 100 numbers between 0 and 1 ");
    printf("to their hexadecimal exponential form (HEF).\n");
    printf("Most of them do not equal their HEFs. That means ");
    printf("that their representations as floats ");
    printf("differ from their actual values.\n");
    double f = 0.01;
    int i;
    for (i = 0; i < 100; i++) {
        printf("%1.2f -> %a\n",f*i,f*i);
    }
    printf("Printing 128 'float-compatible' numbers ");
    printf("together with their HEFs for comparison.\n");
    f = 0x1p-7; // ==0.0071825
    for (i = 0; i < 0x80; i++) {
        printf("%1.7f -> %a\n",f*i,f*i);
    }
    return 0;
}
2
Lars Bohl

Vous pouvez toujours écrire quelque chose comme un type Money pour .Net.

Jetez un oeil à cet article: Un type d'argent pour le CLR - L'auteur a fait un excellent travail à mon avis.

2
Tomer Pintel

J'utilisais le type d'argent de SQL pour stocker des valeurs monétaires. Récemment, j'ai dû travailler avec plusieurs systèmes de paiement en ligne et j'ai remarqué que certains d'entre eux utilisaient des entiers pour stocker des valeurs monétaires. Dans mes projets actuels et nouveaux, j'ai commencé à utiliser des entiers et je suis plutôt content de cette solution.

2
George

Vous voudrez probablement utiliser une forme de représentation en virgule fixe pour les valeurs monétaires. Vous voudrez également examiner l'arrondi de Banker (également connu sous le nom de "arrondi moitié même"). Il évite les biais qui existent dans la méthode habituelle de "arrondi moitié".

1
user6931

Avez-vous envisagé d'utiliser le type money-data pour stocker des montants?

En ce qui concerne Con, cette décimale occupe un octet de plus, je dirais ne vous en souciez pas Sur 1 million de lignes, vous n'utiliserez plus que 1 Mo et le stockage est très bon marché de nos jours.

1
Espo

Quoi que vous fassiez, vous devez faire attention aux erreurs d'arrondi. Calculez avec un degré de précision supérieur à celui affiché.

1
1800 INFORMATION

Vos comptables voudront contrôler comment vous arrondissez. Utiliser float signifie que vous arrondissez constamment, généralement avec une instruction de type FORMAT (), ce qui n'est pas ce que vous voulez faire (utilisez plutôt sol/plafond).

Vous avez des types de données de devise (money, smallmoney), qui doivent être utilisés à la place de float ou real. Stocker des décimales (12,2) éliminera vos arrondis, mais également lors des étapes intermédiaires - ce qui n’est vraiment pas ce que vous souhaiterez dans une application financière.

0
David T. Macknet

C'est un excellent article décrivant quand utiliser float et decimal . Float stocke une valeur approximative et le nombre décimal une valeur exacte.

En résumé, les valeurs exactes telles que money doivent utiliser des valeurs décimales, et les valeurs approximatives telles que des mesures scientifiques, float. 

Voici un exemple intéressant qui montre que les nombres décimaux et flottants sont capables de perdre de la précision. Lorsque vous ajoutez un nombre qui n'est pas un entier, puis que vous soustrayez ce même nombre, le nombre à virgule flottant entraîne une perte de précision, alors que le nombre décimal ne le fait pas

    DECLARE @Float1 float, @Float2 float, @Float3 float, @Float4 float; 
    SET @Float1 = 54; 
    SET @Float2 = 3.1; 
    SET @Float3 = 0 + @Float1 + @Float2; 
    SELECT @Float3 - @Float1 - @Float2 AS "Should be 0";

Should be 0 
---------------------- 
1.13797860024079E-15

Lors de la multiplication d'un nombre non entier et de la division par ce même nombre, les décimales perdent en précision, contrairement aux flottants.

DECLARE @Fixed1 decimal(8,4), @Fixed2 decimal(8,4), @Fixed3 decimal(8,4); 
SET @Fixed1 = 54; 
SET @Fixed2 = 0.03; 
SET @Fixed3 = 1 * @Fixed1 / @Fixed2; 
SELECT @Fixed3 / @Fixed1 * @Fixed2 AS "Should be 1";

Should be 1 
--------------------------------------- 
0.99999999999999900
0
BrokeMyLegBiking

Toujours utiliser Decimal. Float vous donnera des valeurs inexactes en raison de problèmes d'arrondissement.

0
Roel Vlemmings

Les nombres en virgule flottante peuvent seulement représenter des nombres qui sont une somme de multiples négatifs de la base - pour un nombre à virgule flottante binaire, bien sûr, c'est deux.

Il n'y a que quatre fractions décimales représentables précisément en virgule flottante binaire: 0, 0,25, 0,5 et 0,75. Tout le reste est approximatif, de la même manière que 0.3333 ... est une approximation de 1/3 en arithmétique décimale.

La virgule flottante est un bon choix pour les calculs où l’échelle du résultat est ce qui est important. C'est un mauvais choix lorsque vous essayez d'être précis à un certain nombre de décimales.

0
Mike Dimmick