Dans cet article, j'ai constaté que la conversion de 0,1 '' en un nombre à virgule flottante aboutissait à un nombre légèrement supérieur à
0,1 ''.
Pourquoi 0,1 est-il affiché comme 0,1 lors de l'impression même si 0,1 ne peut pas être représenté avec précision par un nombre à virgule flottante
Cela signifie que si vous ajoutez 0,1 '', les erreurs positives devraient s'accumuler. Par exemple, je m'attendais à ce que l'ajout de
0,1 '' 10 fois soit légèrement plus grand que `` 1 ''.
Je vais essayer.
total = 0
for i in range(10):
total += 0.1
print(total)
#=> 0.9999999999999999
De manière inattendue, un nombre inférieur à `` 1 '' a été émis. Cette fois, j'ai étudié ce phénomène.
Cet article utilise Python 3.7.
Dans cet article, le terme «nombre à virgule flottante» fait référence ci-après à «IEEE 754 fois la précision».
Le format de nombre à virgule flottante est une conversion du nombre au format suivant, dans lequel sign
, ʻexp`` et
`frac`` sont classés dans l'ordre.
(-1)^{sign} \times 2^{exp - 1023} \times (1 + frac \times 2^{-52})
Le nom et le nombre de bits de chaque symbole sont les suivants.
symbole | Nom japonais | nom anglais | Nombre de bits |
---|---|---|---|
sign | Partie de code | sign | 1 |
exp | Partie d'index | exponent | 11 |
frac | Partie formelle | fraction | 52 |
`` 0,1 '' est la représentation binaire des nombres à virgule flottante
Code= 0
indice= 01111111011
mantisse= 1001100110011001100110011001100110011001100110011010
Une fois converti en nombre décimal,
Code= 0
indice= 1019
mantisse= 2702159776422298
est. L'exposant à virgule flottante a 1023 '' ajouté, donc en soustrayant cela donne
-4 ''.
Conversion de `` 0,1 '', qui est représenté par un nombre à virgule flottante, en un nombre décimal
0.1000000000000000055511151231257827021181583404541015625
est.
Le nombre à virgule flottante de 0,1 '' est légèrement supérieur à
0,1 '', mais s'il ne devient pas 1 '' en ajoutant 10 fois, il semble qu'un phénomène d'inversion se produit quelque part. Voyons le résultat du calcul en cours de route. Si vous passez une virgule flottante à
Decimal '' et `` print '', la valeur lorsque le nombre à virgule flottante est converti en nombre décimal sera affichée.
from decimal import Decimal
total = 0
d_total = Decimal('0')
for i in range(10):
total += 0.1
d_total += Decimal('0.1')
print(f'{d_total}|{Decimal(total)}')
Voici le résultat de l'exécution.
Décimal | Résultat du calcul |
---|---|
0.1 | 0.1000000000000000055511151231257827021181583404541015625 |
0.2 | 0.200000000000000011102230246251565404236316680908203125 |
0.3 | 0.3000000000000000444089209850062616169452667236328125 |
0.4 | 0.40000000000000002220446049250313080847263336181640625 |
0.5 | 0.5 |
0.6 | 0.59999999999999997779553950749686919152736663818359375 |
0.7 | 0.6999999999999999555910790149937383830547332763671875 |
0.8 | 0.79999999999999993338661852249060757458209991455078125 |
0.9 | 0.899999999999999911182158029987476766109466552734375 |
1.0 | 0.99999999999999988897769753748434595763683319091796875 |
0,1 '' à
0,4 '' sont plus que calculés sous forme de nombres décimaux, 0,5 '' est exactement et
0,6 '' à `` 1,0 '' sont plus que calculés sous forme de nombres décimaux. Le résultat était qu'il était également petit.
Maintenant, vérifions combien est réellement ajouté lors de l'ajout de `` 0,1 '' à la fois. Le programme est ici. Le décimal de Python a 28 chiffres valides par défaut, alors changez-le à une taille suffisante à l'avance.
from decimal import Decimal, getcontext
#Changement du nombre de chiffres valides de Decimal à 64 chiffres
getcontext().prec = 64
total = prev = 0
d_total = Decimal('0')
for i in range(10):
total += 0.1
d_total += Decimal('0.1')
print(f' |{Decimal(total) - Decimal(prev)}')
print(f'{d_total}|{Decimal(total)}')
prev = total
C'est le résultat de l'exécution. Les résultats du calcul utilisant des nombres à virgule flottante sont affichés sur les lignes impaires à l'exclusion de l'en-tête, et les différences sont affichées sur les lignes paires.
Décimal | Résultat du calcul / différence |
---|---|
0.1 | 0.1000000000000000055511151231257827021181583404541015625 |
0.1000000000000000055511151231257827021181583404541015625 |
|
0.2 | 0.200000000000000011102230246251565404236316680908203125 |
0.100000000000000033306690738754696212708950042724609375 |
|
0.3 | 0.3000000000000000444089209850062616169452667236328125 |
0.09999999999999997779553950749686919152736663818359375 |
|
0.4 | 0.40000000000000002220446049250313080847263336181640625 |
0.09999999999999997779553950749686919152736663818359375 |
|
0.5 | 0.5 |
0.09999999999999997779553950749686919152736663818359375 |
|
0.6 | 0.59999999999999997779553950749686919152736663818359375 |
0.09999999999999997779553950749686919152736663818359375 |
|
0.7 | 0.6999999999999999555910790149937383830547332763671875 |
0.09999999999999997779553950749686919152736663818359375 |
|
0.8 | 0.79999999999999993338661852249060757458209991455078125 |
0.09999999999999997779553950749686919152736663818359375 |
|
0.9 | 0.899999999999999911182158029987476766109466552734375 |
0.09999999999999997779553950749686919152736663818359375 |
|
1.0 | 0.99999999999999988897769753748434595763683319091796875 |
La différence entre 0,1 → 0,2 '' est la même que la valeur de
0,1 '' exprimée en nombre à virgule flottante, mais 0,2 → 0,3 '' est plus grande. Après
0,3 '', la même valeur est ajoutée, qui est inférieure à la valeur de `` 0,1 '' exprimée en nombre à virgule flottante.
À propos, toutes les valeurs après 0,4 '' ne sont pas ajoutées moins de
0,1 '', mais augmentent à nouveau avec `` 1,1 → 1,2 ''.
Valeur attendue | Résultat du calcul / différence |
---|---|
1.0 | 0.99999999999999988897769753748434595763683319091796875 |
0.09999999999999997779553950749686919152736663818359375 |
|
1.1 | 1.0999999999999998667732370449812151491641998291015625 |
0.1000000000000000888178419700125232338905334472656250 |
|
1.2 | 1.1999999999999999555910790149937383830547332763671875 |
0.1000000000000000888178419700125232338905334472656250 |
|
1.3 | 1.3000000000000000444089209850062616169452667236328125 |
0.1000000000000000888178419700125232338905334472656250 |
|
1.4 | 1.4000000000000001332267629550187848508358001708984375 |
0.1000000000000000888178419700125232338905334472656250 |
|
1.5 | 1.5000000000000002220446049250313080847263336181640625 |
Pourquoi 0,1 '' n'augmente-t-il pas du nombre de points flottants lors de l'ajout de
0,1 ''?
Cela implique des erreurs d'arrondi.
L'addition de nombres à virgule flottante positifs est effectuée par la procédure suivante.
① Faites correspondre l'index le plus petit avec l'index le plus grand (2) Ajustez en réduisant le nombre incorrect à mesure que l'exposant augmente. ③ Ajoutez les numéros incorrects ④ En cas de report, ajoutez 1 à l'exposant et réduisez le pseudonyme en conséquence. ⑤ Arrondissez les chiffres débordants du nombre incorrect (même arrondi)
La procédure d'ajout de nombres à virgule flottante est illustrée à l'aide d'un exemple de nombre décimal. Considérez l'ajout de nombres décimaux à 5 chiffres.
\begin{array}{llcll}
&9.8192 & \times & 10^2 & (= 981.92)\\
+ &4.7533 & \times & 10^1 & (= 47.533)\\
\hline
\end{array}
La procédure est expliquée ci-dessous.
① Faites correspondre l'index le plus petit avec l'index le plus grand (2) Ajustez en réduisant le nombre incorrect à mesure que l'exposant augmente.
Alignez les exposants pour l'ajout. Cette fois, celui avec l'index le plus grand est 9.8192 x 10 ^ 2
, donc nous allons transformer 4.7533 x 10 ^ 1
pour l'adapter.
\begin{array}{llcr}
&4.7533 & \times & 10^1\\
= &0.47533 & \times & 10^2
\end{array}
③ Ajoutez les numéros incorrects Additionner.
\begin{array}{llcr}
&\phantom{1}9.8192 & \times & 10^2\\
+ &\phantom{1}0.47533 & \times & 10^2\\
\hline
& 10.29453 & \times & 10^2
\end{array}
④ En cas de report, ajoutez 1 à l'exposant et réduisez le pseudonyme en conséquence.
Puisque la partie entière est 10, le report se produit. Pour faire de la partie entière un chiffre, ajoutez 1 à l'exposant et divisez le nombre incorrect par 10.
\begin{array}{llcr}
&10.29453 & \times & 10^2\\
= &1.029453 & \times & 10^3
\end{array}
⑤ Autour de la poutre débordante (même ronde)
Cette fois, le nombre de chiffres valides est de 5, nous devons donc considérer le traitement du `` 53 '' de fin. Puisque même l'arrondi est utilisé ici, il sera arrondi.
\begin{array}{llcr}
&1.029453 & \times & 10^3\\
\rightarrow &1.0295 & \times & 10^3
\end{array}
La procédure d'ajout de nombres à virgule flottante a été confirmée en nombres décimaux.
L'arrondi uniforme est une méthode d'arrondi similaire à l'arrondi, mais lorsque l'objet à arrondir se trouve exactement au milieu des deux valeurs, il est arrondi vers le haut ou vers le bas pour que le chiffre supérieur soit pair.
Par exemple, en arrondissant un nombre pair au premier chiffre, 1,5 '' devient
2 '' et 4,5 '' devient
4 ''.
4,51 '' est
5 ''.
Dans l'exemple d'ajout en décimal, le dernier chiffre (`` 53 '' dans l'exemple ci-dessus) débordait des chiffres effectifs lorsque les exposants étaient combinés. Ces valeurs doivent être mises de côté car elles seront arrondies à la fin. (Ne tronquez pas simplement parce que l'exposant est augmenté et que le nombre incorrect est diminué et ajusté à l'étape 2).
Avec même arrondi,
-Plus de 5 → Arrondir ―― 5 Parfait → Arrondir vers le haut ou vers le bas dans la même direction ―― Moins de 5 → Tronquer
Par conséquent, nous avons besoin d'informations sur les chiffres en dessous ainsi que sur le chiffre le plus significatif des chiffres qui débordent. Cependant, ce qui est nécessaire pour les informations sur les chiffres autres que le chiffre le plus significatif est de savoir s'il s'agit ou non exactement de 0, il semble donc suffisant de préparer une valeur booléenne.
Les chiffres de garde sont la zone pour stocker les chiffres qui débordent. Pour l'ajout de nombres positifs, un chiffre de protection suffit. (Un bit de plus est nécessaire pour traiter la soustraction et les nombres négatifs). Si le chiffre de protection est un chiffre, dans l'exemple ci-dessus, le bit le plus significatif 5 '' des chiffres de débordement
53 '' est stocké.
Une valeur booléenne qui indique si un nombre inférieur ou égal au chiffre de protection contient un nombre supérieur ou égal à 1 est appelée un bit persistant. Dans l'exemple ci-dessus, vrai '' (ou
1 '') est défini car le chiffre de débordement `` 3 '' est 1 ou plus.
Appliquez les chiffres de protection et les bits persistants à l'exemple d'addition décimale ci-dessus.
\begin{array}{llcll}
&9.8192 & \times & 10^2 & (= 981.92)\\
+ &4.7533 & \times & 10^1 & (= 47.533)\\
\hline
\end{array}
① Faites correspondre l'index le plus petit avec l'index le plus grand (2) Ajustez en réduisant le nombre incorrect à mesure que l'exposant augmente.
\begin{array}{lrcll}
&4.7533 & \times & 10^1&\\
= &0.47533 & \times & 10^2&(Poutre de protection=3)
\end{array}
Depuis que le dernier `` 3 '' a débordé, je l'ai sauvegardé dans le chiffre de protection.
③ Ajoutez les numéros incorrects
\begin{array}{llcr}
&\phantom{1}9.8192 & \times & 10^2&\\
+ &\phantom{1}0.4753 & \times & 10^2&(Poutre de protection=3)\\
\hline
& 10.2945 & \times & 10^2&(Poutre de protection=3)
\end{array}
④ En cas de report, ajoutez 1 à l'exposant et réduisez le pseudonyme en conséquence.
\begin{array}{llcrl}
&10.2945 & \times & 10^2&\\
= &1.0294 & \times & 10^3&(Poutre de protection=5,Bit collant=true)
\end{array}
Le 5 '' de fin a débordé, je l'ai donc sauvegardé dans le chiffre de protection. À ce stade,
3 '', qui était à l'origine enregistré dans le chiffre de protection, est enregistré dans le bit collant. Le bit collant tient si le nombre est 1 ou plus, donc `` vrai '' est défini.
⑤ Autour de la poutre débordante (même ronde) Utilisez la poutre de protection et le mors collant pour arrondir, L'arrondissement est effectué car le bit collant a été vrai `` avec le chiffre de protection 00 ''.
\begin{array}{llcrl}
&1.0294 & \times & 10^3&(Poutre de protection=5,Bit collant=true)\\
\rightarrow &1.0295 & \times & 10^3
\end{array}
La procédure d'ajout de nombres à virgule flottante à l'aide de chiffres protégés et de bits persistants a été confirmée en nombres décimaux. Faites de même pour les nombres binaires.
Puisque c'est devenu long, je vais diviser l'article. Dans la deuxième partie, nous vérifierons le comportement lors de l'ajout de `` 0,1 ''. Les sites auxquels j'ai fait référence sont également résumés dans la deuxième partie.
Recommended Posts