Exemples d'imprécision de la virgule flottante

Comment expliquez-vous l'inexactitude de la virgule flottante aux nouveaux programmeurs et profanes qui pensent encore que les ordinateurs sont infiniment sages et précis?

Avez-vous un exemple ou une anecdote préférée qui semble faire passer l'idée bien mieux qu'une explication précise, mais sèche?

Comment cela est-il enseigné dans les cours D'Informatique?

29
demandé sur David Rutten 2010-01-20 13:10:30

7 réponses

il y a essentiellement deux pièges majeurs où les gens tombent avec des nombres à virgule flottante.

  1. Le problème de l'échelle. Chaque nombre FP a un exposant qui détermine l ' "échelle" globale du nombre de sorte que vous pouvez représenter des valeurs vraiment petites ou très grandes, bien que le nombre de chiffres que vous pouvez consacrer pour cela est limité. Si l'on ajoute deux nombres d'échelle différente, le plus petit sera parfois "mangé" puisqu'il n'est pas une façon de le tenir dans la plus grande échelle.

    PS> $a = 1; $b = 0.0000000000000000000000001
    PS> Write-Host a=$a b=$b
    a=1 b=1E-25
    PS> $a + $b
    1
    

    Comme une analogie pour ce cas, vous pourriez l'image d'une grande piscine et une cuillère à café d'eau. Les deux sont de tailles très différentes, mais individuellement vous pouvez facilement saisir combien ils sont grossièrement. Verser la cuillère à café dans la piscine, cependant, vous laissera toujours avec près d'une piscine pleine d'eau.

    (si les gens qui apprennent cela ont des problèmes avec la notation exponentielle, un pouvez également utiliser les valeurs 1 et 100000000000000000000 .)

  2. puis il y a le problème de la représentation binaire vs. décimale. Un nombre comme 0.1 ne peut pas être représenté exactement avec un nombre limité de chiffres binaires. Certaines langues masquent cela, cependant:

    PS> "{0:N50}" -f 0.1
    0.10000000000000000000000000000000000000000000000000
    

    mais vous pouvez "amplifier" l'erreur de représentation en ajoutant plusieurs fois les nombres ensemble:

    PS> $sum = 0; for ($i = 0; $i -lt 100; $i++) { $sum += 0.1 }; $sum
    9,99999999999998
    

    Je ne peux pas pensez à une belle analogie pour bien expliquer cela, cependant. C'est essentiellement le même problème pourquoi vous pouvez représenter 1 / 3 seulement approximativement en décimal parce que pour obtenir la valeur exacte vous devez répéter les 3 indéfiniment à la fin de la fraction décimale.

    de même, les fractions binaires sont bonnes pour représenter les moitiés, les quarts, les huitièmes, etc. mais des choses comme un dixième donnera une infinie répétition de flux binaire chiffre.

  3. alors il y a un autre problème, bien que la plupart des gens ne trébuchent pas là-dessus, à moins qu'ils ne fassent d'énormes quantités de choses numériques. Mais alors, ceux-là savent déjà à propos du problème. Comme de nombreux nombres à virgule flottante ne sont que des approximations de la valeur exacte, cela signifie que pour une approximation donnée f d'un nombre réel r il peut y avoir infiniment plus de nombres réels r 1 , r 2 , ... la carte à exactement le même rapprochement. Ces nombres se trouvent dans un certain intervalle. Disons que r min est la valeur minimum possible de r qui se traduit par f et r max la valeur maximale possible de r pour lequel cela tient, alors vous avez un intervalle [ r min , r max ] où n'importe quel nombre dans cet intervalle peut être votre nombre réel r .

    maintenant, si vous effectuez des calculs sur ce nombre-addition, soustraction, multiplication, etc.-vous perdre de la précision. Chaque nombre n'est qu'une approximation, donc vous exécutez calculs avec intervalles . Le résultat est un intervalle aussi et l'erreur d'approximation ne devient jamais plus grand, élargissant ainsi l'intervalle. Vous pouvez récupérer un seul nombre de ce calcul. Mais c'est tout simplement un nombre de l'intervalle de possible résultats, en tenant compte de la précision de vos opérandes originales et la perte de précision due au calcul.

    ce genre de chose est appelé arithmétique intervalle et au moins pour moi, il a fait partie de notre cours de mathématiques à l'Université.

26
répondu Јοеу 2010-01-20 17:00:18

leur montrer que le système de base-10 souffre de exactement le même problème.

essayez de représenter 1/3 comme une représentation décimale en base 10. Vous ne serez pas en mesure de le faire exactement.

donc si vous écrivez" 0.3333", vous aurez une représentation raisonnablement exacte pour de nombreux cas d'utilisation.

Mais si vous déplacez le retour à une fraction, vous obtiendrez "3333/10000", qui est pas l' même en tant que "1/3".

D'autres fractions, telles que 1/2 peuvent facilement être représentées par une représentation décimale finie dans la base-10:" 0.5 "

maintenant la base-2 et la base-10 souffrent essentiellement du même problème: tous deux ont des nombres qu'ils ne peuvent pas représenter exactement.

alors que la base-10 n'a aucun problème à représenter 1/10 comme" 0.1 "dans la base-2 vous auriez besoin d'une représentation infinie commençant par" 0.000110011..".

8
répondu Joachim Sauer 2010-01-20 12:23:51

comment c'est pour une explantation au profane. Les ordinateurs représentent les nombres en comptant les unités discrètes. Ce sont des ordinateurs numériques. Pour les nombres entiers, ceux sans partie fractionnaire, les ordinateurs numériques modernes comptent des puissances de deux: 1, 2, 4, 8. ,, Valeur Place, nombres binaires, blah, blah, blah. Pour les fractions, les ordinateurs numériques comptent des puissances inverses de deux: 1/2, 1/4, 1/8,... Le problème est que de nombreux numéros ne peuvent pas être représentés par une somme d'un nombre fini de ces inverse pouvoirs. Utiliser plus de valeurs de place (plus de bits) augmentera la précision de la représentation de ces nombres 'problèmes', mais ne l'obtient jamais exactement parce qu'il a seulement un nombre limité de bits. Certains numéros ne peuvent pas être représentés avec un nombre infini de bits.

Snooze...

OK, vous voulez mesurer le volume d'eau dans un récipient, et vous avez seulement 3 tasses à mesurer: pleine tasse, demi-tasse, et quart de tasse. Après avoir compté le dernier plein tasse, disons qu'il reste un tiers de tasse. Pourtant, vous ne pouvez pas mesurer cela parce qu'il ne remplit pas exactement n'importe quelle combinaison de tasses disponibles. Il ne remplit pas la demi-tasse, et le débordement de la Quart-tasse est trop petit pour remplir quoi que ce soit. Si vous avez une erreur - la différence entre 1/3 et 1/4. Cette erreur est aggravée lorsque vous la combinez avec des erreurs provenant d'autres mesures.

6
répondu gary 2010-01-20 12:13:10

en python:

>>> 1.0 / 10
0.10000000000000001

expliquer comment certaines fractions ne peuvent pas être représentées précisément en binaire. Tout comme certaines fractions (comme 1/3) ne peuvent pas être représentées précisément en base 10.

2
répondu codeape 2010-01-20 10:14:48

un autre exemple, dans C

printf (" %.20f \n", 3.6);

donne incroyablement

3.60000000000000008882

2
répondu cibercitizen1 2011-05-13 07:46:40

voici ma simple compréhension.

problème: La valeur 0,45 ne peut pas être représentée avec précision par un flotteur et est arrondie à 0,450000018. Pourquoi est-ce?

réponse: Une valeur int de 45 est représentée par la valeur binaire 101101. Afin de faire la valeur 0.45 il serait précis si elle vous pouvez prendre 45 x 10^-2 (= 45 / 10^2.) Mais c'est impossible parce que vous devez utiliser la base 2 au lieu de 10.

ainsi le le plus proche de 10^2 = 100 serait 128 = 2^7. Le nombre total de bits dont vous avez besoin est 9 : 6 pour la valeur 45 (101101) + 3 bits pour la valeur 7 (111). Puis la valeur 45 x 2^-7 = 0,3515625. Maintenant, vous avez un grave inexactitude problème. 0,3515625 n'est pas proche de 0,45.

comment améliorer cette imprécision? On pourrait changer les valeurs 45 et 7 pour autre chose.

Que diriez-vous de 460 x 2^-10 = 0.44921875. Vous utilisez maintenant 9 bits pour 460 et 4 bits pour 10. Ensuite, c'est un peu plus près, mais pas encore pour tout de suite. Cependant, si votre valeur initiale désirée était 0.44921875, alors vous obtiendriez une correspondance exacte sans approximation.

donc la formule pour votre valeur serait X = A x 2^B. où A et B sont des valeurs entières positives ou négatives. Évidemment, plus le nombre peut être plus votre précision deviennent cependant, comme vous le savez le nombre de bits pour représenter les valeurs de A et B sont limitées. Pour float vous avez un nombre total de 32. Le Double a 64 et le Décimal a 128.

1
répondu Jan 2013-04-16 08:01:51

un joli morceau de bizarrerie numérique peut être observé si on convertit 9999999.4999999999 en un float et retour à un double . Le résultat est rapporté comme 10000000, même si cette valeur est évidemment plus proche de 9999999, et même si 9999999999 tourne correctement à 9999999.

0
répondu supercat 2013-02-05 03:02:02