web-dev-qa-db-fra.com

Qu'est-ce qui cause les erreurs d'arrondi en virgule flottante?

Je sais que l'arithmétique à virgule flottante a des problèmes de précision. Je les surmonte généralement en passant à une représentation décimale fixe du nombre, ou tout simplement en négligeant l'erreur.

Cependant, je ne sais pas quelles sont les causes de cette inexactitude. Pourquoi y a-t-il tant de problèmes d'arrondi avec les nombres flottants?

66
nmat

En effet, certaines fractions ont besoin d'une très grande (voire infinie) place pour s'exprimer sans arrondir. Cela vaut autant pour la notation décimale que pour la binaire ou toute autre. Si vous limitez la quantité de décimales à utiliser pour vos calculs (et évitez de faire des calculs en notation fractionnaire), vous devrez arrondir même une expression simple en 1/3 + 1/3. Au lieu d'écrire 2/3, vous devez écrire 0,33333 + 0,33333 = 0,66666, ce qui n'est pas identique à 2/3.

Dans le cas d'un ordinateur, le nombre de chiffres est limité par la nature technique de sa mémoire et de ses registres CPU. La notation binaire utilisée en interne ajoute quelques difficultés supplémentaires. Les ordinateurs ne peuvent normalement pas exprimer les nombres en notation fractionnaire, bien que certains langages de programmation ajoutent cette capacité, ce qui permet d'éviter ces problèmes dans une certaine mesure.

Ce que tout informaticien devrait savoir sur l'arithmétique en virgule flottante

84
thorsten müller

Principalement, les erreurs d'arrondi proviennent du fait que l'infinité de tous les nombres réels ne peut pas être représenté par la mémoire finie d'un ordinateur, sans parler d'une petite tranche de mémoire telle qu'une seule variable à virgule flottante, donc de nombreux nombres stockés ne sont que des approximations du nombre qu'ils sont censés représenter.

Puisqu'il n'y a qu'un nombre limité de valeurs qui sont pas une approximation, et toute opération entre une approximation et un un autre nombre donne une approximation, les erreurs d'arrondi sont presque inévitables.

L'important est de savoir quand ils sont susceptibles de causer un problème et prendre des mesures pour atténuer les risques.


En plus de David Goldberg est essentiel Ce que tout informaticien devrait savoir sur l'arithmétique en virgule flottante (republié par Sun/Oracle en annexe à leur Numerical Computation Guide ), mentionné par thorsten , le - ACCU journal Surcharge a publié une excellente série d'articles par Richard Harris sur les Floating Point Blues.

La série a commencé avec

L'informatique numérique a de nombreux pièges. Richard Harris commence à chercher une balle d'argent.

Le dragon de l'erreur numérique n'est pas souvent réveillé de son sommeil, mais s'il est approché avec précaution, il causera occasionnellement des dommages catastrophiques aux calculs imprudents du programmeur.

À tel point que certains programmeurs, après l'avoir rencontré par hasard dans les forêts de l'arithmétique à virgule flottante IEEE 754, déconseillent à leurs camarades de voyager dans ce beau pays.

Dans cette série d'articles, nous explorerons le monde de l'informatique numérique, en contrastant l'arithmétique à virgule flottante avec certaines des techniques qui ont été proposées comme remplacements plus sûrs. Nous apprendrons que le territoire du dragon est en effet d'une grande portée et qu'en général nous devons marcher prudemment si nous craignons son attention dévastatrice.

Richard commence par expliquer la taxonomie des nombres réels, rationnels, irrationnels, algébriques et transcendantaux. Il explique ensuite la représentation IEEE754, avant de passer à l'erreur d'annulation et aux problèmes d'ordre d'exécution.

Si vous ne lisez pas plus profondément que cela, vous aurez une excellente mise à la terre dans les problèmes associés aux nombres à virgule flottante.

Si vous voulez en savoir plus cependant, il continue avec

Il passe ensuite à essayer de vous aider à guérir votre Calculus Blues

et enfin et surtout, il y a

Toute la série d'articles mérite d'être examinée et, à 66 pages au total, elles sont toujours plus petites que les 77 pages du article Goldberg .

Bien que cette série couvre à peu près le même terrain, je l'ai trouvée plutôt plus accessible que celle de Goldberg papier . J'ai également trouvé plus facile de comprendre les parties les plus complexes de l'article après avoir lu le premier des articles de Richards et après ces premiers articles, Richard se diversifie dans de nombreux domaines intéressants qui ne sont pas abordés par l'article de Goldberg.


Comme parle ainsi a.k. mentionné dans les commentaires:

En tant qu'auteur de ces articles, je voudrais mentionner que j'en ai créé des versions interactives sur mon blog www.thusspakeak.com commençant par thusspakeak.com/ak/2013/06 .

74
Mark Booth

Eh bien, thorsten a le définitif link . J'ajouterais:

Toute forme de représentation aura une erreur d'arrondi pour un certain nombre. Essayez d'exprimer 1/3 en virgule flottante IEEE ou en décimal. Ni l'un ni l'autre ne peut le faire avec précision. Cela va au-delà de la réponse à votre question, mais j'ai utilisé cette règle empirique avec succès:

  • Stockez les valeurs saisies par l'utilisateur en décimal (car ils l'ont presque certainement entré en représentation décimale - très peu d'utilisateurs utiliseront le binaire ou l'hexagone). De cette façon, vous avez toujours la représentation exacte saisie par l'utilisateur.
  • Si vous devez stocker des fractions saisies par l'utilisateur, stockez le numérateur et le dénominateur (également en décimal)
  • Si vous avez un système avec plusieurs unités de mesure pour la même quantité (comme Celsius/Fahrenheit), et que l'utilisateur peut entrer les deux, stocker la valeur qu'ils ont entrée et les unités dans lesquelles ils les ont entrés. N'essayez pas de convertir et d'enregistrer sous une seule représentation, sauf si vous pouvez le faire sans perte de précision/exactitude. Utilisez les valeurs enregistrées et dans tous les calculs.
  • Stockez les valeurs générées par la machine en virgule flottante IEEE (il peut s'agir de nombres générés par un appareil de mesure électronique, comme un capteur analogique avec un convertisseur A/N, ou le résultat non arrondi d'un calcul). Notez que cela ne s'applique pas si vous lisez un capteur sur une connexion série et qu'il vous donne déjà la valeur au format décimal (par exemple 18,2 C).
  • Stockez les totaux visibles par l'utilisateur, etc., en décimal (comme un solde de compte bancaire). Arrondissez de manière appropriée, mais utilisez cette valeur comme valeur définitive pour tous les calculs futurs.
12
Scott Whitlock

Ce qui ne semble pas avoir été mentionné jusqu'à présent, ce sont les concepts d'un algorithme instable et d'un problème mal conditionné. Je vais d'abord aborder le premier, car cela semble être un écueil plus fréquent pour les numéricistes débutants.

Considérons le calcul des puissances du nombre d'or (réciproque) φ=0.61803…; une façon possible de procéder consiste à utiliser la formule de récursivité φ^n=φ^(n-2)-φ^(n-1), en commençant par φ^0=1 et φ^1=φ. Si vous exécutez cette récursivité dans votre environnement informatique préféré et comparez les résultats avec des puissances évaluées avec précision, vous constaterez une lente érosion des chiffres significatifs. Voici ce qui se passe par exemple dans Mathematica:

ph = N[1/GoldenRatio];  
Nest[Append[#1, #1[[-2]] - #1[[-1]]] & , {1, ph}, 50] - ph^Range[0, 51]  
{0., 0., 1.1102230246251565*^-16, -5.551115123125783*^-17, 2.220446049250313*^-16, 
-2.3592239273284576*^-16, 4.85722573273506*^-16, -7.147060721024445*^-16, 
1.2073675392798577*^-15, -1.916869440954372*^-15, 3.1259717037102064*^-15, 
-5.0411064211886014*^-15, 8.16837916750579*^-15, -1.3209051907825398*^-14, 
2.1377864756200182*^-14, -3.458669982359108*^-14, 5.596472721011714*^-14, 
-9.055131861349097*^-14, 1.465160458236081*^-13, -2.370673237795176*^-13, 
3.835834102607072*^-13, -6.206507137114341*^-13, 1.004234127360273*^-12, 
-1.6248848342954435*^-12, 2.6291189633497825*^-12, -4.254003796798193*^-12, 
6.883122762265558*^-12, -1.1137126558640235*^-11, 1.8020249321541067*^-11, 
-2.9157375879969544*^-11, 4.717762520172237*^-11, -7.633500108148015*^-11, 
1.23512626283229*^-10, -1.9984762736468268*^-10, 3.233602536479646*^-10, 
-5.232078810126407*^-10, 8.465681346606119*^-10, -1.3697760156732426*^-9, 
2.216344150333856*^-9, -3.5861201660070964*^-9, 5.802464316340953*^-9, 
-9.388584482348049*^-9, 1.5191048798689004*^-8, -2.457963328103705*^-8, 
3.9770682079726053*^-8, -6.43503153607631*^-8, 1.0412099744048916*^-7, 
-1.6847131280125227*^-7, 2.725923102417414*^-7, -4.4106362304299367*^-7, 
7.136559332847351*^-7, -1.1547195563277288*^-6}

Le résultat supposé pour φ^41 A le mauvais signe, et même plus tôt, les valeurs calculées et réelles pour φ^39 Ne partagent aucun chiffre en commun (3.484899258054952 * ^ - 9 for the computed version against the true value 7.071019424062048 *^-9). L'algorithme est donc instable, et il ne faut pas utiliser cette formule de récursivité en arithmétique inexacte. Cela est dû à la nature inhérente de la formule de récursivité: il existe une solution "en décomposition" et "croissante" à cette récursivité, et essayer de calculer la solution "en décomposition" par la solution directe lorsqu'il existe une solution alternative "croissante" est la mendicité pour le chagrin numérique. Il faut donc s'assurer que ses algorithmes numériques sont stables.

Maintenant, sur le concept d'un problème mal conditionné: même s'il existe un moyen stable de faire quelque chose numériquement, il se peut très bien que le problème que vous avez ne puisse tout simplement pas être résolu par votre algorithme. . C'est la faute du problème lui-même, et non la méthode de résolution. L'exemple canonique en numérique est la solution d'équations linéaires impliquant la soi-disant "matrice de Hilbert":

Hilbert matrix

La matrice est l'exemple canonique d'une matrice mal conditionnée: essayer de résoudre un système avec une grande matrice de Hilbert pourrait retourner une solution inexacte.

Voici une démonstration Mathematica: comparez les résultats de l'arithmétique exacte

Table[LinearSolve[HilbertMatrix[n], HilbertMatrix[n].ConstantArray[1, n]], {n, 2, 12}]
{{1, 1}, {1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 
  1}, {1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1,
   1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 
  1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}

et arithmétique inexacte

Table[LinearSolve[N[HilbertMatrix[n]], N[HilbertMatrix[n].ConstantArray[1, n]]], {n, 2, 12}]
{{1., 1.}, {1., 1., 1.}, {1., 1., 1., 1.}, {1., 1., 1., 1., 1.},  
  {1., 1., 1., 1., 1., 1.}, {1., 1., 1., 1., 1., 1., 1.}, 
  {1., 1., 1., 1., 1., 1., 1., 1.}, {1., 1., 1., 1., 1., 1., 1., 1., 1.},  
  {1., 1., 1., 0.99997, 1.00014, 0.999618, 1.00062, 0.9994, 1.00031, 
  0.999931}, {1., 1., 0.999995, 1.00006, 0.999658, 1.00122, 0.997327, 
  1.00367, 0.996932, 1.00143, 0.999717}, {1., 1., 0.999986, 1.00022, 
  0.998241, 1.00831, 0.975462, 1.0466, 0.94311, 1.04312, 0.981529, 
  1.00342}}

(Si vous l'avez essayé dans Mathematica, vous noterez quelques messages d'erreur avertissant de l'apparition de mauvais conditionnements.)

Dans les deux cas, une simple augmentation de la précision n'est pas un remède; cela ne fera que retarder l'érosion inévitable des chiffres.

C'est à cela que vous pourriez être confronté. Les solutions peuvent être difficiles: pour la première, soit vous retournez à la planche à dessin, soit vous vous promenez dans des journaux/livres/quoi que ce soit pour trouver si quelqu'un d'autre a trouvé une meilleure solution que vous; pour le second, vous abandonnez ou reformulez votre problème en quelque chose de plus maniable.


Je vous laisse avec une citation de Dianne O'Leary:

La vie peut nous jeter des problèmes mal conditionnés, mais il n'y a aucune bonne raison de se contenter d'un algorithme instable.

10
user1372

car les nombres décimaux en base 10 ne peuvent pas être exprimés en base 2

ou en d'autres termes 1/10 ne peut pas être transformé en une fraction avec une puissance de 2 dans le dénominateur (ce qui sont essentiellement les nombres à virgule flottante)

9
ratchet freak

En mathématiques, il existe une infinité de nombres rationnels. Une variable 32 bits ne peut avoir que 232 différentes valeurs, et une variable 64 bits seulement 264 valeurs. Par conséquent, il existe une infinité de nombres rationnels qui n'ont pas de représentation précise.

Nous pourrions trouver des schémas qui nous permettraient de représenter 1/3 parfaitement, ou 1/100. Il s'avère que pour de nombreuses raisons pratiques, cela n'est pas très utile. Il y a une grande exception: en finance, les fractions décimales apparaissent souvent. C'est principalement parce que la finance est essentiellement une activité humaine, pas physique.

Par conséquent, nous choisissons généralement d'utiliser une virgule flottante binaire et d'arrondir toute valeur qui ne peut pas être représentée en binaire. Mais en finance, nous choisissons parfois la virgule flottante décimale et arrondissons les valeurs à la valeur décimale la plus proche.

9
MSalters