Question

Nous réécrivons 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 les avantages et les inconvénients de l'un ou l'autre?

L'un des inconvénients mentionnés dans notre journal quotidien était que vous devez faire attention lorsque vous calculez un montant renvoyant 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 indiquant deux décimales. J'ai remarqué à quelques reprises que cela n'était pas fait et que les montants ne semblaient pas corrects. (c'est-à-dire 10.2 ou 10.2546)

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

Était-ce utile?

La solution

  

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 selon IEEE 754 toujours binaires, uniquement les nouvelle norme IEEE 754R définition décimale formats. 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 entiers positifs), tout nombre décimal sous la forme m/(2^n*5^n).
Comme les fichiers binaires ne contiennent pas le premier factor 5, 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? Arrondissement.
Arrondir normalement signifie 0..4 en bas, 5..9 en haut. Donc importe si le résultat est 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 pure chance. Cela devient sans espoir si vous voulez une manipulation décimale arrondie 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 prenez chacun des nombres formatés donnés pour chaque entrée, analysez-les 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 requises (nous avons utilisé le logiciel Oracle).   FLOAT) afin que nous puissions enregistrer le & Quot; milliardième d’un penny & "; être accured.

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 manière indépendante.

Autres conseils

Tout d'abord, vous devriez lire ceci Ce que tout informaticien devrait savoir sur l'arithmétique en virgule flottante . Ensuite, vous devriez vraiment envisager d’utiliser un type de package à point fixe / nombre arbitraire-précis (par exemple: java BigNum, module décimal python) sinon vous serez 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 que vous ne compensiez pas totalement leurs limitations.

À titre d’avertissement supplémentaire, SQL Server et le framework .Net utilisent un algorithme par défaut différent pour l’arrondi. Veillez à extraire le paramètre MidPointRounding dans Math.Round (). La structure .Net utilise l’algorithme Bankers par défaut et SQL Server utilise l’arrondi algorithmique symétrique. Consultez l'article sur Wikipedia ici

.

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.

Dans le logiciel de comptabilité n’est PAS acceptable un float. Utilisez décimal avec 4 décimales.

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 réalité stockés sous forme de valeur binaire et d’une puissance de 2 exposants.

Donc 1.5 est stocké sous la forme 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 quelque chose comme: 1.0999999999989

Cela est dû au fait que la représentation binaire de 1.1 est en réalité 154811237190861 x 2 ^ -47, plus qu'un double ne peut gérer.

En savoir plus sur ce problème sur mon blog , mais en gros, pour le stockage, il vaut mieux utiliser des décimales.

Sur le serveur Microsoft SQL, vous disposez du money type de données - 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 deviendra rapidement importante.

Cependant, les nombres décimaux ne sont pas très utiles pour les mathématiques - il n’existe pas de support natif pour les pouvoirs décimaux, par exemple.

Utilisez le type décimal du serveur SQL.

N'utilisez pas money ou float .

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

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

Un peu d’arrière-plan 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 les virgules flottantes IEEE standard et les décimales signées. La virgule flottante IEEE est plus précise par bit utilisé, mais cela n’a aucune importance ici.

Les chiffres financiers sont basés sur des siècles de pratique papier-crayon, 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 celle qui donne la même réponse qu’un CPA qui est doué en arithmétique. C’est une arithmétique décimale, pas une virgule flottante IEEE.

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

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 avec exactitude, tandis que le nombre flottant n’utilisera pas de 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

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

Pour ce calcul, une précision et une fidélité extrêmes étaient nécessaires (nous avons utilisé le logiciel FLOAT d'Oracle) pour pouvoir enregistrer le & "milliardième d'un penny &"; en cours d’exécution.

En ce qui concerne " capitalizing " l’intérêt (c’est-à-dire le remboursement de l’intérêt sur votre compte), le montant a été arrondi au centime. Le type de données pour les soldes de compte était à deux décimales. (En fait, c'était plus compliqué, car il s'agissait d'un système multidevises pouvant fonctionner avec plusieurs décimales - mais nous arrondissions toujours au & "Penny &" De cette devise). Oui - là où & Quot; fractions & Quot; de perte et de gain, mais lorsque les chiffres des ordinateurs ont été actualisés (argent versé ou versé), il s'agissait toujours de valeurs d'argent RÉELLES.

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

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

Encore 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, ce qui se présente comme suit:

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, telle que: 10 ^ n, où n est le nombre de décimales.

Une autre chose dont vous devez être conscient 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, seuls deux d'entre elles devraient avoir cet accès.

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

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

Jetez un coup d'œil à cet article: Un type Money pour le CLR - L’auteur a fait un excellent travail à mon avis.

J'utilisais le type d'argent de SQL pour stocker des valeurs monétaires. Récemment, j'ai dû travailler avec un certain nombre de 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.

Sur les 100 fractions n / 100, où n est un nombre naturel tel que 0 < = n et n < 100, seuls quatre peuvent être représentés sous forme de nombres à virgule flottante. Regardez le résultat 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;
}

Avez-vous envisagé d'utiliser le type de données monétaire pour stocker des montants en dollars?

En ce qui concerne le dés que la 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.

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

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

Vos comptables voudront contrôler votre arrondi. Utiliser float signifie que vous allez constamment arrondir, 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.

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

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

Il n’existe que quatre fractions décimales pouvant être représentées 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 si vous essayez d’être précis à un certain nombre de décimales.

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 les mesures scientifiques doivent utiliser 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, les valeurs en décote sont perdues, alors que les valeurs décimales ne le sont 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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top