Que peut-on faire aux langages de programmation pour éviter les pièges à virgule flottante?

28

L'incompréhension de l'arithmétique à virgule flottante et de ses défauts est une cause majeure de surprise et de confusion dans la programmation (considérez le nombre de questions sur Stack Overflow concernant "les nombres ne s'ajoutent pas correctement"). Étant donné que de nombreux programmeurs n'ont pas encore compris ses implications, il a le potentiel d'introduire de nombreux bogues subtils (en particulier dans les logiciels financiers). Que peuvent faire les langages de programmation pour éviter ses pièges pour ceux qui ne connaissent pas les concepts, tout en offrant sa vitesse lorsque la précision est pas critique pour ceux qui ne comprennent les concepts?

Adam Paynter
la source
26
La seule chose qu'un langage de programmation puisse faire pour éviter les pièges du traitement en virgule flottante est de l'interdire. Notez que cela inclut également la virgule flottante en base 10, ce qui est tout aussi inexact en général, sauf que les applications financières y sont pré-adaptées.
David Thornley
4
C'est à cela que sert «l'analyse numérique». Apprenez à minimiser les pertes de précision - les pièges à virgule flottante.
Un bon exemple d'un problème de virgule flottante: stackoverflow.com/questions/10303762/0-0-0-0-0
Austin Henley

Réponses:

47

Vous dites "spécialement pour les logiciels financiers", ce qui évoque une de mes bêtes noires: l' argent n'est pas un flotteur, c'est un int .

Bien sûr, cela ressemble à un flotteur. Il contient un point décimal. Mais c'est simplement parce que vous êtes habitué aux unités qui confondent le problème. L'argent vient toujours en quantités entières. En Amérique, c'est des cents. (Dans certains contextes, je pense que ce peut être des moulins , mais ignorez cela pour l'instant.)

Donc, quand vous dites 1,23 $, c'est vraiment 123 cents. Toujours, toujours, faites toujours vos calculs en ces termes, et tout ira bien. Pour plus d'informations, voir:

Pour répondre directement à la question, les langages de programmation devraient simplement inclure un type Money comme primitive raisonnable.

mise à jour

D'accord, j'aurais dû dire "toujours" deux fois plutôt que trois fois. L'argent est en effet toujours un int; ceux qui pensent le contraire sont invités à m'envoyer 0,3 centime et à me montrer le résultat sur votre relevé bancaire. Mais comme le soulignent les commentateurs, il existe de rares exceptions lorsque vous devez effectuer des calculs à virgule flottante sur des nombres de type monétaire. Par exemple, certains types de prix ou de calcul des intérêts. Même alors, ceux-ci devraient être traités comme des exceptions. L'argent entre et sort sous forme de quantités entières, donc plus votre système s'en rapproche, plus il sera sain.

William Pietri
la source
20
@JoelFan: vous confondez un concept avec une implémentation spécifique à une plateforme.
whatsisname
12
Ce n'est pas aussi simple que cela. Les calculs d'intérêts, entre autres, produisent des fractions de centimes et doivent être arrondis à un moment donné selon une méthode spécifiée.
kevin cline
24
Fictional -1, car il me manque le représentant pour un downvote :) ... Cela pourrait être correct pour tout ce qui se trouve dans votre portefeuille, mais il existe de nombreuses situations comptables où vous pourriez bien avoir affaire à des dixièmes de cent ou à des fractions plus petites. Decimalest le seul système sensé pour faire face à cela, et votre commentaire "ignorer cela pour l'instant" est le signe avant-coureur du destin pour les programmeurs du monde
entier
9
@kevin cline: Il y a des cents fractionnaires dans les calculs, mais il existe des conventions sur la façon de les gérer. Le but des calculs financiers n'est pas l'exactitude mathématique, mais d'obtenir exactement les mêmes résultats qu'un banquier avec une calculatrice.
David Thornley
6
Tout sera parfait en remplaçant le mot "entier" par "rationnel" -
Emilio Garavaglia
15

La prise en charge d'un type décimal est utile dans de nombreux cas. De nombreuses langues ont un type décimal, mais elles sont sous-utilisées.

Il est important de comprendre l'approximation qui se produit lorsque vous travaillez avec la représentation de nombres réels. L'utilisation de types décimaux et à virgule flottante 9 * (1/9) != 1est une instruction correcte. Lorsque les constantes, un optimiseur peut optimiser le calcul afin qu'il soit correct.

Fournir un opérateur approximatif serait utile. Cependant, ces comparaisons sont problématiques. Notez que .9999 billion de dollars est approximativement égal à 1 billion de dollars. Pourriez-vous s'il vous plaît déposer la différence sur mon compte bancaire?

BillThor
la source
2
0.9999...mille milliards de dollars équivaut précisément à mille milliards de dollars.
JUSTE MON AVIS correct
5
@JUST: Oui, mais je n'ai rencontré aucun ordinateur avec des registres pouvant contenir 0.99999.... Ils sont tous tronqués à un moment donné, ce qui entraîne une inégalité. 0.9999est suffisamment égal pour l'ingénierie. Pour des raisons financières, ce n'est pas le cas.
BillThor
2
Mais quel type de système utilisait des billions de dollars comme unité de base au lieu de dollars?
Brad
@Brad Essayez de calculer (1 billion / 3) * 3 sur votre calculatrice. Quelle valeur obtenez-vous?
BillThor
8

On nous a dit quoi faire lors de la première année (deuxième année) de cours en informatique lorsque je suis allé à l'université (ce cours était également un pré-requis pour la plupart des cours de sciences)

Je me souviens du conférencier disant "Les nombres à virgule flottante sont des approximations. Utilisez des types entiers pour de l'argent. Utilisez FORTRAN ou une autre langue avec des nombres BCD pour un calcul précis." (puis il a souligné l'approximation, en utilisant cet exemple classique de 0,2 impossible à représenter avec précision en virgule flottante binaire). Cela est également apparu cette semaine dans les exercices de laboratoire.

Même cours: "Si vous devez obtenir plus de précision à partir de virgule flottante, triez vos termes. Ajoutez des petits nombres ensemble, pas des grands nombres." Cela m'est resté dans l'esprit.

Il y a quelques années, j'avais une géométrie sphérique qui devait être très précise et toujours rapide. Le double de 80 bits sur les PC ne le coupait pas, j'ai donc ajouté quelques types au programme qui triaient les termes avant d'effectuer des opérations commutatives. Problème résolu.

Avant de vous plaindre de la qualité de la guitare, apprenez à jouer.

J'ai eu un collègue il y a quatre ans qui avait travaillé pour JPL. Il a exprimé son incrédulité que nous avons utilisé FORTRAN pour certaines choses. (Nous avions besoin de simulations numériques super précises calculées hors ligne.) "Nous avons remplacé tout ce FORTRAN par du C ++", a-t-il déclaré fièrement. J'ai arrêté de me demander pourquoi ils avaient raté une planète.

Tim Williscroft
la source
2
+1 le bon outil pour le bon travail. Bien que je n'utilise pas réellement FORTRAN. Heureusement, je ne travaille pas non plus sur nos systèmes financiers au travail.
James Khoury
"Si vous devez obtenir plus de précision à partir de virgule flottante, triez vos termes. Ajoutez de petits nombres ensemble, pas de gros nombres." Un échantillon à ce sujet?
mamcx
@mamcx Imaginez un nombre décimal à virgule flottante ayant un seul chiffre de précision. Le calcul 1.0 + 0.1 + ... + 0.1(répété 10 fois) revient 1.0lorsque chaque résultat intermédiaire est arrondi. Faire l'inverse, vous obtenez des résultats intermédiaires 0.2, 0.3..., 1.0et enfin 2.0. C'est un exemple extrême, mais avec des nombres à virgule flottante réalistes, des problèmes similaires se produisent. L'idée de base est que l'ajout de nombres de taille similaire entraîne la plus petite erreur. Commencez avec les plus petits nombres car leur somme est plus grande et donc mieux adaptée pour être ajoutée à de plus grands.
maaartinus
Les éléments en virgule flottante dans Fortran et C ++ seront cependant essentiellement identiques. Les deux sont précis et hors ligne, et je suis presque sûr que Fortran n'a pas de réels BCD natifs ...
Mark
8

Avertissement: Le type à virgule flottante System.Double manque de précision pour les tests d'égalité directs.

double x = CalculateX();
if (x == 0.1)
{
    // ............
}

Je ne crois pas que quelque chose puisse ou doive être fait au niveau linguistique.

ChaosPandion
la source
1
Je n'ai pas utilisé de flotteur ou de double depuis longtemps, donc je suis curieux. S'agit-il d'un véritable avertissement du compilateur existant, ou simplement celui que vous aimeriez voir?
Karl Bielefeldt
1
@Karl - Personnellement, je ne l'ai pas vu ni n'en ai besoin, mais j'imagine que cela pourrait être utile à des développeurs dévoués mais verts.
ChaosPandion
1
Les types binaires à virgule flottante ne sont pas meilleurs ou pires qualitativement que Decimallorsqu'il s'agit de tests d'égalité. La différence entre 1.0m/7.0m*7.0met 1.0mpeut être de plusieurs ordres de grandeur inférieure à la différence entre 1.0/7.0*7.0, mais elle n'est pas nulle.
supercat
1
@Patrick - Je ne suis pas sûr de ce que vous voulez dire. Il y a une énorme différence entre ce qui est vrai pour un cas et vrai pour tous les cas.
ChaosPandion
1
@ChaosPandion Le problème avec l'exemple dans ce post n'est pas la comparaison d'égalité, c'est le littéral à virgule flottante. Il n'y a pas de flotteur avec la valeur exacte 1.0 / 10. Les calculs en virgule flottante donnent des résultats précis à 100% lors du calcul avec des nombres entiers ajustés dans la mantisse.
Patrick
7

Par défaut, les langues doivent utiliser des logiques de précision arbitraire pour les nombres non entiers.

Ceux qui ont besoin d'optimiser peuvent toujours demander des flotteurs. Les utiliser par défaut était logique dans C et d'autres langages de programmation de systèmes, mais pas dans la plupart des langages populaires aujourd'hui.

Waquo
la source
1
Comment gérez-vous alors les nombres irrationnels?
dsimcha
3
Vous le faites de la même manière qu'avec les flottants: approximation.
Waquo
1
Je dois dire que je pense que cela a beaucoup de sens, la plupart des gens qui ont besoin de nombres exacts ont besoin de rationnels et non d'irrationnels (la science et l'ingénierie peuvent utiliser des irrationnels mais vous êtes à nouveau dans le domaine approximatif, ou vous faites des mathématiques pures assez spécialisées)
jk.
1
Les calculs avec des justifications de précision arbitraire seront souvent des ordres de grandeur plus lents (peut-être BEAUCOUP d'ordres de grandeur plus lents) que les calculs avec un support matériel double. Si un calcul doit être précis à une partie par million, il est préférable de passer une microseconde à le calculer à quelques parties par milliard plutôt que de passer une seconde à le calculer de façon absolument précise.
supercat
5
@supercat: Ce que vous proposez n'est qu'un poster-child d'optimisation prématurée. La situation actuelle est que la grande majorité des programmeurs n'ont absolument aucun besoin de mathématiques rapides, puis se font mordre par un comportement (mal) à virgule flottante difficile à comprendre, de sorte que le nombre relativement faible de programmeurs qui ont besoin de mathématiques rapides l'obtient sans avoir pour taper un seul caractère supplémentaire. Cela avait du sens dans les années 70, maintenant ce n'est plus qu'un non-sens. La valeur par défaut doit être sûre. Ceux qui ont besoin de rapidité devraient le demander.
Waquo
4

Les deux plus gros problèmes concernant les nombres à virgule flottante sont:

  • unités incohérentes appliquées aux calculs (notez que cela affecte également l'arithmétique entière de la même manière)
  • l'incapacité à comprendre que les nombres FP sont une approximation et comment gérer intelligemment l'arrondi.

Le premier type de défaillance ne peut être résolu qu'en fournissant un type composite qui inclut des informations de valeur et d'unité. Par exemple, une valeur lengthou areaqui incorpore l'unité (mètres ou mètres carrés ou pieds et pieds carrés respectivement). Sinon, vous devez faire preuve de diligence pour toujours travailler avec un type d'unité de mesure et ne convertir en un autre que lorsque nous partageons la réponse avec un humain.

Le deuxième type d'échec est un échec conceptuel. Les échecs se manifestent lorsque les gens les considèrent comme des nombres absolus . Elle affecte les opérations d'égalité, les erreurs d'arrondi cumulées, etc. Par exemple, il peut être correct que pour un système deux mesures soient équivalentes dans une certaine marge d'erreur. C'est à dire .999 et 1.001 sont à peu près les mêmes que 1.0 lorsque vous ne vous souciez pas des différences inférieures à +/- 0,1. Cependant, tous les systèmes ne sont pas aussi indulgents.

S'il y a une facilité de niveau de langue nécessaire, je l'appellerais précision d'égalité . Dans NUnit, JUnit et les frameworks de test construits de manière similaire, vous pouvez contrôler la précision considérée comme correcte. Par exemple:

Assert.That(.999, Is.EqualTo(1.001).Within(10).Percent);
// -- or --
Assert.That(.999, Is.EqualTo(1.001).Within(.1));

Si, par exemple, C # ou Java ont été modifiés pour inclure un opérateur de précision, cela pourrait ressembler à ceci:

if(.999 == 1.001 within .1) { /* do something */ }

Cependant, si vous fournissez une fonctionnalité comme celle-ci, vous devez également considérer le cas où l'égalité est bonne si les côtés +/- ne sont pas les mêmes. Par exemple, + 1 / -10 considérerait deux nombres équivalents si l'un d'eux était à 1 de plus ou 10 de moins que le premier nombre. Pour gérer ce cas, vous devrez peut-être également ajouter un rangemot clé:

if(.999 == 1.001 within range(.001, -.1)) { /* do something */ }
Berin Loritsch
la source
2
Je changerais l'ordre. Le problème conceptuel est omniprésent. Le problème de conversion des unités est relativement mineur en comparaison.
S.Lott
J'aime le concept d'un opérateur de précision, mais comme vous le mentionnez plus loin, il devrait certainement être bien pensé. Personnellement, je serais plus enclin à le voir comme sa propre construction syntaxique complète.
ChaosPandion
Cela pourrait aussi très facilement se faire dans une bibliothèque.
Michael K
1
@ dan04: Je pensais davantage en termes de "tous les calculs précis à 1% près" ou similaires. J'ai vu la fosse à goudron qui est une unité de mesure et je reste loin.
TMN
1
Il y a environ 25 ans, j'ai vu un package numérique comportant un type composé d'une paire de nombres à virgule flottante représentant les valeurs maximales et minimales possibles pour une quantité. Au fur et à mesure que les nombres passaient par les calculs, la différence entre le maximum et le minimum augmenterait. En effet, cela a fourni un moyen de savoir combien de précision réelle était présente dans une valeur calculée.
supercat
3

Que peuvent faire les langages de programmation? Je ne sais pas s'il y a une réponse à cette question, car tout ce que le compilateur / interprète fait au nom du programmeur pour lui faciliter la vie va généralement à l'encontre des performances, de la clarté et de la lisibilité. Je pense que la méthode C ++ (ne payez que ce dont vous avez besoin) et la méthode Perl (principe de la moindre surprise) sont toutes deux valides, mais cela dépend de l'application.

Les programmeurs doivent encore travailler avec le langage et comprendre comment il gère les virgules flottantes, car s'ils ne le font pas, ils formuleront des hypothèses et un jour, le comportement préconisé ne correspondra pas à leurs hypothèses.

Mon point de vue sur ce que le programmeur doit savoir:

  • Quels types de virgule flottante sont disponibles sur le système et dans la langue
  • Quel type est nécessaire
  • Comment exprimer les intentions de quel type est nécessaire dans le code
  • Comment profiter correctement de toute promotion de type automatique pour équilibrer la clarté et l'efficacité tout en conservant l'exactitude
John
la source
3

Que peuvent faire les langages de programmation pour éviter les pièges [à virgule flottante] ...?

Utilisez des valeurs par défaut raisonnables, par exemple un support intégré pour les decmials.

Groovy le fait très bien, mais avec un peu d'effort, vous pouvez toujours écrire du code pour introduire une imprécision en virgule flottante.

Armand
la source
3

Je suis d'accord qu'il n'y a rien à faire au niveau linguistique. Les programmeurs doivent comprendre que les ordinateurs sont discrets et limités, et que bon nombre des concepts mathématiques qui y sont représentés ne sont que des approximations.

Peu importe la virgule flottante. Il faut comprendre que la moitié des modèles de bits sont utilisés pour des nombres négatifs et que 2 ^ 64 est en fait assez petit pour éviter les problèmes typiques avec l'arithmétique entière.

Apalala
la source
en désaccord, la plupart des langages donnent actuellement trop de support pour les types binaires à virgule flottante (pourquoi est == même défini pour les flottants?) et pas assez de support pour les rationnels ou les décimales
jk.
@jk: Même si le résultat d'un calcul ne serait jamais garanti égal au résultat d'un autre calcul, la comparaison d'égalité serait toujours utile dans le cas où la même valeur est affectée à deux variables (bien que les règles d'égalité couramment implémentées soient peut-être trop lâche, car x== yn'implique pas que l'exécution d'un calcul sur xdonnera le même résultat que l'exécution du même calcul sur y).
supercat
@supercat, vous avez toujours besoin d'une comparaison, mais je préfère que le langage me demande de spécifier une tolérance pour chaque comparaison à virgule flottante, je peux alors revenir à l'égalité en choisissant tolérance = 0, mais je suis au moins obligé de le faire choix
jk.
3

Une chose que les langages pourraient faire - supprimer la comparaison d'égalité des types à virgule flottante autre qu'une comparaison directe aux valeurs NAN.

Le test d'égalité n'existerait que comme appel de fonction qui a pris les deux valeurs et un delta, ou pour des langages comme C # qui permettent aux types d'avoir des méthodes un EqualsTo qui prend l'autre valeur et le delta.

Loren Pechtel
la source
3

Je trouve étrange que personne n'ait souligné le numéro rationnel de la famille Lisp.

Sérieusement, ouvrez sbcl, et faites ceci: (+ 1 3)et vous obtenez 4. Si vous le faites, *( 3 2)vous obtenez 6. Maintenant, essayez (/ 5 3)et vous obtenez 5/3, ou 5 tiers.

Cela devrait aider quelque peu dans certaines situations, n'est-ce pas?

Haakon Løtveit
la source
Je me demande, si possible de savoir si un résultat doit être représenté comme 1/3 ou pourrait être une décimale exacte?
mamcx
bonne suggestion
Peter Porfy
3

Une chose que je voudrais voir serait une reconnaissance du fait que doublede floatdevrait être considérée comme une conversion élargissement, alors que floatpour doublese rétrécit (*). Cela peut sembler contre-intuitif, mais réfléchissez à la signification réelle des types:

  • 0,1 f signifie "13 421 773,5 / 134 217 728, plus ou moins 1/268 435 456 environ".
  • 0,1 signifie réellement 3 602 879 701 896 397/36 028 797 018 963 968, plus ou moins 1/72 057 594 037 927 936 environ "

Si l'on a un doublequi détient la meilleure représentation de la quantité "un dixième" et le convertit en float, le résultat sera "13 421 773,5 / 134 217 728, plus ou moins 1/268 435 456 environ", qui est une description correcte de la valeur.

En revanche, si l'on a un floatqui détient la meilleure représentation de la quantité "un dixième" et le convertit en double, le résultat sera "13 421 773,5 / 134 217 728, plus ou moins 1/72 057 594 097 927 936 environ" - un niveau de précision implicite ce qui est faux d'un facteur de plus de 53 millions.

Bien que la norme IEEE-744 exige que les calculs à virgule flottante soient effectués comme si chaque nombre à virgule flottante représente la quantité numérique exacte précisément au centre de sa plage, cela ne devrait pas être interprété comme impliquant que les valeurs à virgule flottante représentent réellement ces valeurs exactes. quantités numériques. Au contraire, l'exigence selon laquelle les valeurs doivent être supposées être au centre de leurs plages découle de trois faits: (1) les calculs doivent être effectués comme si les opérandes avaient des valeurs précises particulières; (2) des hypothèses cohérentes et documentées sont plus utiles que des hypothèses incohérentes ou non documentées; (3) si l'on veut faire une hypothèse cohérente, aucune autre hypothèse cohérente n'est susceptible d'être meilleure que de supposer qu'une quantité représente le centre de sa fourchette.

Soit dit en passant, je me souviens il y a environ 25 ans, quelqu'un est venu avec un paquet numérique pour C qui utilisait des "types de plage", chacun consistant en une paire de flottants de 128 bits; tous les calculs seraient effectués de manière à calculer la valeur minimale et maximale possible pour chaque résultat. Si l'on effectuait un grand calcul itératif long et arrivait à une valeur de [12.53401391134 12.53902812673], on pouvait être sûr que même si de nombreux chiffres de précision étaient perdus en raison d'erreurs d'arrondi, le résultat pouvait toujours être raisonnablement exprimé comme 12,54 (et ce n'était pas le cas '' t vraiment 12,9 ou 53,2). Je suis surpris de n'avoir vu aucun support pour de tels types dans les langages traditionnels, d'autant plus qu'ils semblent convenir aux unités mathématiques qui peuvent fonctionner sur plusieurs valeurs en parallèle.

(*) Dans la pratique, il est souvent utile d'utiliser des valeurs à double précision pour effectuer des calculs intermédiaires lorsque vous travaillez avec des nombres à simple précision, donc avoir à utiliser un transtypage pour toutes ces opérations peut être ennuyeux. Les langues pourraient aider en ayant un type "double flou", qui effectuerait des calculs en double, et pourrait être librement casté vers et depuis un simple; cela serait particulièrement utile si les fonctions qui prennent des paramètres de type doubleet de retour doublepouvaient être marquées de manière à générer automatiquement une surcharge qui accepte et renvoie à la place un "double flou".

2 tours
la source
2

Si davantage de langages de programmation prenaient une page des bases de données et permettaient aux développeurs de spécifier la longueur et la précision de leurs types de données numériques, ils pourraient réduire considérablement la probabilité d'erreurs liées aux virgules flottantes. Si un langage permettait à un développeur de déclarer une variable comme un flottant (2), indiquant qu'il avait besoin d'un nombre à virgule flottante avec deux chiffres décimaux de précision, il pourrait effectuer des opérations mathématiques beaucoup plus en toute sécurité. S'il le faisait en représentant la variable sous forme d'entier en interne et en divisant par 100 avant d'exposer la valeur, il pourrait améliorer la vitesse en utilisant les chemins arithmétiques d'entier plus rapides. La sémantique d'un Float (2) permettrait également aux développeurs d'éviter le besoin constant d'arrondir les données avant de les produire car un Float (2) arrondirait intrinsèquement les données à deux décimales.

Bien sûr, vous devez autoriser un développeur à demander une valeur à virgule flottante de précision maximale lorsque le développeur doit avoir cette précision. Et vous introduiriez des problèmes où des expressions légèrement différentes de la même opération mathématique produisent des résultats potentiellement différents en raison d'opérations d'arrondi intermédiaires lorsque les développeurs n'ont pas assez de précision dans leurs variables. Mais au moins dans le monde des bases de données, cela ne semble pas être un problème trop important. La plupart des gens ne font pas le genre de calculs scientifiques qui nécessitent beaucoup de précision dans les résultats intermédiaires.

Justin Cave
la source
Spécifier la longueur et la précision ne ferait pas grand-chose d'utile. Avoir une base à virgule fixe 10 serait utile pour le traitement financier, ce qui éliminerait une grande partie de la surprise que les gens obtiennent de la virgule flottante.
David Thornley
@David - Peut-être que je manque quelque chose, mais en quoi un type de données à base fixe 10 est-il différent de ce que je propose ici? Un flottant (2) dans mon exemple aurait un nombre fixe de 2 chiffres décimaux et serait automatiquement arrondi au centième le plus proche, ce que vous utiliseriez probablement pour de simples calculs financiers. Des calculs plus complexes nécessiteraient que le développeur alloue un plus grand nombre de chiffres décimaux.
Justin Cave
1
Ce que vous préconisez est un type de données à base fixe 10 avec une précision spécifiée par le programmeur. Je dis que la précision spécifiée par le programmeur est pour la plupart inutile et mènera juste au genre d'erreurs que je rencontrais dans les programmes COBOL. (Par exemple, lorsque vous modifiez la précision des variables, il est vraiment facile de manquer une variable dans laquelle passe la valeur. Pour une autre, il faudra beaucoup plus de réflexion sur la taille du résultat intermédiaire que sur la bonne.)
David Thornley
4
Un Float(2)comme vous proposez ne doit pas être appelé Float, car il n'y a rien flottant ici, certainement pas le "point décimal".
Paŭlo Ebermann
1
  • les langues ont un support de type décimal; bien sûr, cela ne résout pas vraiment le problème, vous n'avez toujours pas de représentation exacte et finie de par exemple ⅓;
  • certaines bases de données et frameworks prennent en charge le type Money, il s'agit essentiellement de stocker le nombre de cents sous forme d'entier;
  • il existe des bibliothèques pour la prise en charge des nombres rationnels; qui résout le problème de ⅓, mais ne résout pas le problème de par exemple √2;

Celles-ci sont applicables dans certains cas, mais pas vraiment une solution générale pour traiter les valeurs flottantes. La vraie solution est de comprendre le problème et d'apprendre à y faire face. Si vous utilisez des calculs de virgule flottante, vous devez toujours vérifier si vos algorithmes sont numériquement stables . Il existe un vaste domaine de mathématiques / informatique qui se rapporte au problème. C'est ce qu'on appelle l' analyse numérique .

vartec
la source
1

Comme d'autres réponses l'ont noté, le seul véritable moyen d'éviter les pièges à virgule flottante dans les logiciels financiers est de ne pas les utiliser là-bas. Cela peut être possible - si vous fournissez une bibliothèque bien conçue dédiée aux mathématiques financières .

Les fonctions conçues pour importer des estimations en virgule flottante doivent être clairement étiquetées comme telles et dotées de paramètres appropriés à cette opération, par exemple:

Finance.importEstimate(float value, Finance roundingStep)

La seule véritable façon d'éviter les pièges à virgule flottante en général est l'éducation - les programmeurs doivent lire et comprendre quelque chose comme ce que chaque programmeur devrait savoir sur l'arithmétique à virgule flottante .

Quelques choses qui pourraient aider, cependant:

  • J'appuie ceux qui demandent "pourquoi le test d'égalité exact en virgule flottante est-il même légal?"
  • Utilisez plutôt une isNear()fonction.
  • Fournir et encourager l'utilisation d'objets accumulateurs à virgule flottante (qui ajoutent des séquences de valeurs à virgule flottante de manière plus stable que de simplement les ajouter tous dans une variable à virgule flottante normale).
tempête à venir
la source
-1

La plupart des programmeurs seraient surpris que COBOL ait bien compris ... dans la première version de COBOL, il n'y avait pas de virgule flottante, seulement décimale, et la tradition dans COBOL a continué jusqu'à aujourd'hui que la première chose à laquelle vous pensez lorsque vous déclarez un nombre est décimal. .. la virgule flottante ne serait utilisée que si vous en aviez vraiment besoin. Lorsque C est arrivé, pour une raison quelconque, il n'y avait pas de type décimal primitif, donc à mon avis, c'est là que tous les problèmes ont commencé.

JoelFan
la source
1
C n'avait pas de type décimal car il n'est pas primitif, très peu d'ordinateurs ont des instructions décimales matérielles. Vous pourriez vous demander pourquoi BASIC et Pascal ne l'avaient pas, car ils n'étaient pas conçus pour se conformer étroitement au métal. COBOL et PL / I sont les seules langues que je connaisse de l'époque qui avaient quelque chose comme ça.
David Thornley
3
@JoelFan: alors comment écrivez-vous ⅓ en COBOL? Decimal ne résout aucun problème, la base 10 est tout aussi inexacte que la base 2.
vartec
2
Decimal résout le problème de la représentation exacte des dollars et des cents, ce qui est utile pour un langage "orienté entreprise". Mais sinon, la décimale est inutile; il présente les mêmes types d'erreurs (par exemple, 1/3 * 3 = 0,99999999) tout en étant beaucoup plus lent. C'est pourquoi ce n'est pas la langue par défaut dans les langues qui n'ont pas été spécifiquement conçues pour la comptabilité.
dan04
1
Et FORTRAN, qui est antérieur à C de plus d'une décennie, n'a pas non plus de prise en charge décimale standard.
dan04
1
@JoelFan: si vous avez une valeur trimestrielle et que vous avez besoin d'une valeur mensuelle, devinez ce que vous devez multiplier par ... non, ce n'est pas 0,33, c'est ⅓.
vartec