Plusieurs questions ont été posées à SO sur la représentation en virgule flottante. Par exemple, le nombre décimal 0.1 n'a pas de représentation binaire exacte. Il est donc dangereux d'utiliser l'opérateur == pour le comparer à un autre nombre à virgule flottante. Je comprends les principes de la représentation en virgule flottante.
Ce que je ne comprends pas, c’est pourquoi d’un point de vue mathématique, les chiffres à droite du point décimal sont-ils plus "spéciaux" que ceux à gauche?
Par exemple, le nombre 61.0 a une représentation binaire exacte car la partie intégrale de tout nombre est toujours exacte. Mais le nombre 6.10 n'est pas exact. Je n’ai fait que déplacer la virgule d’une position et je suis soudainement passé d’Exactopia à Inexactville. Mathématiquement, il ne devrait y avoir aucune différence intrinsèque entre les deux nombres - ce sont juste des nombres.
En revanche, si je déplace le point décimal d’une position dans l’autre direction pour produire le nombre 610, je suis toujours dans Exactopia. Je peux continuer dans cette direction (6100, 610000000, 610000000000000) et ils sont toujours exacts, exacts, exacts. Mais dès que la virgule dépasse un certain seuil, les chiffres ne sont plus exacts.
Que se passe-t-il?
Edit: pour clarifier les choses, je tiens à rester en dehors de toute discussion sur les représentations standard, telles que IEEE, et à rester fidèle à ce que je considère être la méthode mathématique "pure". En base 10, les valeurs de position sont:
... 1000 100 10 1 1/10 1/100 ...
En binaire, ils seraient:
... 8 4 2 1 1/2 1/4 1/8 ...
Il n'y a pas non plus de limite arbitraire placée sur ces nombres. Les positions augmentent indéfiniment à gauche et à droite.
Les nombres décimaux peuvent être représentés exactement, si vous avez assez d’espace - mais pas par des nombres flottants binaires points. Si vous utilisez un type virgule décimale virgule flottante (par exemple, System.Decimal
dans .NET), de nombreuses valeurs ne pouvant pas être représentées exactement sous forme de virgule flottante binaire peuvent être exactement représentées.
Regardons les choses sous un autre angle: en base 10, avec lequel vous êtes probablement à l'aise, vous ne pouvez pas exprimer exactement 1/3. C'est 0.3333333 ... (récurrent). La raison pour laquelle vous ne pouvez pas représenter 0,1 sous forme de nombre à virgule flottante est exactement la même. Vous pouvez représenter 3 et 9 et 27 exactement - mais pas 1/3, 1/9 ou 1/27.
Le problème est que 3 est un nombre premier qui n'est pas un facteur de 10. Ce n'est pas un problème si vous voulez multiplier un nombre par 3: vous pouvez toujours multiplier par un entier sans rencontrer de problèmes. . Mais quand vous divisez par un nombre premier et qui n’est pas un facteur de votre base, vous pouvez avoir des ennuis (et le fera faites-le si vous essayez de diviser 1 par ce nombre).
Bien que 0,1 soit généralement utilisé comme exemple le plus simple d’un nombre décimal exact ne pouvant pas être représenté exactement en virgule flottante binaire, on peut dire que 0,2 est un exemple plus simple, car il est 1/5 - et 5 est le premier qui pose des problèmes entre décimal et binaire. .
Certains types de virgule décimale flottante ont une taille fixe comme System.Decimal
d'autres comme Java.math.BigDecimal
sont "arbitrairement grands" - mais ils atteindront une limite à un moment donné, qu'il s'agisse de la mémoire système ou de la taille maximale théorique d'un objet. tableau. Il s’agit toutefois d’un point totalement distinct du point principal de cette réponse. Même si vous aviez un nombre réellement arbitrairement grand de bits à jouer, vous ne pouviez toujours pas représenter le nombre décimal 0,1 exactement dans une représentation à virgule flottante. Comparez cela avec l'inverse: étant donné un nombre arbitraire de chiffres décimaux, vous pouvez représentez exactement tout nombre pouvant être représenté comme un point binaire flottant.
La raison de l’imprécision est la nature des bases numériques. En base 10, vous ne pouvez pas représenter exactement 1/3. Il devient 0,333 ... Cependant, en base 3, 1/3 est exactement représenté par 0,1 et 1/2 est un nombre décimal qui se répète à l'infini (tresimal?). Les valeurs pouvant être finement représentées dépendent du nombre de facteurs premiers uniques de la base. Ainsi, la base 30 [2 * 3 * 5] peut représenter plus de fractions que la base 2 ou la base 10. Encore plus pour la base 210 [2 * 3 * 5 * 7].
Ceci est un problème distinct de "l'erreur de virgule flottante". L’inexactitude est due au fait que quelques milliards de valeurs sont réparties sur une plage beaucoup plus large. Donc, si vous avez 23 bits pour le significande, vous ne pouvez représenter qu’environ 8,3 millions de valeurs distinctes. Ensuite, un exposant de 8 bits fournit 256 options pour la distribution de ces valeurs. Ce schéma permet que les décimales les plus précises se produisent près de 0. Vous pouvez donc presque représenter 0,1.
Par exemple, le nombre 61.0 a une représentation binaire exacte car la partie intégrale de tout nombre est toujours exacte. Mais le nombre 6.10 n'est pas exact. Je n’ai fait que déplacer la virgule d’une position et je suis soudainement passé d’Exactopia à Inexactville. Mathématiquement, il ne devrait y avoir aucune différence intrinsèque entre les deux nombres - ce sont juste des nombres.
Éloignons-nous un instant des particularités des bases 10 et 2. Demandons-nous - dans la base b
, quels nombres ont des représentations finales et quels nombres n'en ont pas? Un instant de pensée nous dit qu'un nombre x
a une représentation finale b
si et seulement s'il existe un entier n
tel que _x b^n
_ est un entier.
Ainsi, par exemple, _x = 11/500
_ a une représentation finale de 10, car nous pouvons choisir _n = 3
_ puis _x b^n = 22
_, un entier. Cependant _x = 1/3
_ ne le fait pas, car quoi que nous fassions pour n
, nous ne pourrons pas nous débarrasser du 3.
Ce deuxième exemple nous incite à réfléchir aux facteurs, et nous pouvons voir que pour tout rationnel _x = p/q
_ (supposé être en termes les plus bas), nous pouvons répondre à la question en comparant les factorisations principales de b
et q
. Si q
a des facteurs premiers autres que la factorisation de b
, nous ne serons jamais en mesure de trouver un n
apte à supprimer ces facteurs.
Ainsi pour la base 10, tout _p/q
_ où q
a des facteurs premiers autres que 2 ou 5 n'aura pas de représentation finale.
Revenons maintenant aux bases 10 et 2, nous voyons que tout rationnel avec une représentation finale de 10 sera de la forme _p/q
_ exactement lorsque q
n’a que _2
_ s et _5
_ s dans sa principale factorisation; et ce même nombre aura une représentation finale 2 exactement quand q
n'aura que _2
_ s dans sa factorisation initiale.
Mais l'un de ces cas est un sous-ensemble de l'autre! N'importe quand
q
n'a que _2
_ s dans sa factorisation principale
il est évidemment également vrai que
q
a seulement _2
_ s et _5
_ s dans sa factorisation principale
ou, autrement dit, chaque fois que _p/q
_ a une représentation finale 2, _p/q
_ a une représentation finale 1. Cependant, l'inverse n'a pas pas - chaque fois que q
a un 5 dans sa factorisation initiale, il aura une représentation finale de 10, mais pas une terminaison 2. C'est l'exemple _0.1
_ mentionné par d'autres réponses.
Nous avons donc la réponse à votre question - parce que les facteurs premiers de 2 sont un sous-ensemble des facteurs premiers de 10, tous les nombres de 2 terminaisons sont des nombres de 10 terminaisons, mais pas l’inverse. C’est pas environ 61 contre 6,1 - c'est environ 10 contre 2.
En guise de conclusion, si par certains bizarreries les gens utilisaient (disons) la base 17 mais que nos ordinateurs utilisaient la base 5, votre intuition n’aurait jamais été égarée - il y aurait non nombres (non nuls, non entiers) terminés dans les deux cas!
La raison fondamentale (mathématique) est que lorsque vous avez affaire à des entiers, ils sont infiniment infinis .
Ce qui signifie que, même s'il en existe une quantité infinie, nous pourrions "compter" tous les éléments de la séquence, sans en ignorer aucun. Cela signifie que si nous voulons obtenir l’élément à la _ 610000000000000
e position dans la liste, nous pouvons le résoudre à l’aide d’une formule.
Cependant, les nombres réels sont infiniment infinis . Vous ne pouvez pas dire "donnez-moi le nombre réel à la position 610000000000000
" et obtenez une réponse. La raison en est que même entre 0
et 1
, il existe un nombre infini de valeurs lorsque vous envisagez des valeurs à virgule flottante. Il en va de même pour deux nombres en virgule flottante.
Plus d'informations:
http://en.wikipedia.org/wiki/Countable_set
http://en.wikipedia.org/wiki/Uncountable_set
Mise à jour: Mes excuses, il semble que j'ai mal interprété la question. Ma réponse concerne la raison pour laquelle nous ne pouvons pas représenter chaque valeur réelle. Je n'avais pas réalisé que la virgule flottante était automatiquement classée comme rationnelle.
Pour répéter ce que j’ai dit dans mon commentaire à M. Skeet: nous pouvons représenter 1/3, 1/9, 1/27 ou tout autre raisonnement rationnel. notation décimale. Nous le faisons en ajoutant un symbole supplémentaire. Par exemple, une ligne sur les chiffres qui se répètent dans le développement décimal du nombre. Ce dont nous avons besoin pour représenter les nombres décimaux en tant que séquence de nombres binaires est 1) une séquence de nombres binaires, 2 ) un point de base, et 3) un autre symbole pour indiquer la partie répétée de la séquence.
La notation de citation de Hehner est un moyen de le faire. Il utilise un symbole de citation pour représenter la partie répétée de la séquence. L'article: http://www.cs.toronto.edu/~hehner/ratno.pdf et l'entrée Wikipedia: http://en.wikipedia.org/wiki/Quote_notation .
Rien ne dit que nous ne pouvons pas ajouter de symbole à notre système de représentation. Nous pouvons donc représenter les rationnels décimaux exactement en utilisant la notation de citation binaire, et inversement.
BCD - Décimal codé en binaire - les représentations sont exactes. Ils ne sont pas très efficaces en termes d'espace, mais c'est un compromis que vous devez faire pour la précision dans ce cas.
Si vous faites un nombre assez grand avec une virgule flottante (comme cela peut être le cas des exposants), alors vous vous retrouverez avec une inexactitude devant le point décimal. Donc, je ne pense pas que votre question soit tout à fait valable parce que la prémisse est fausse; ce n'est pas le cas que le décalage de 10 créera toujours plus de précision, car à un moment donné, le nombre à virgule flottante devra utiliser des exposants pour représenter l'ampleur du nombre et perdra également de la précision.
C'est la même raison pour laquelle vous ne pouvez pas représenter exactement 1/3 en base 10, vous devez dire 0.33333 (3). En binaire, c'est le même type de problème, mais cela ne se produit que pour différents ensembles de nombres.
(Note: Je vais ajouter 'b' pour indiquer les nombres binaires ici. Tous les autres nombres sont donnés en décimal)
Une façon de penser aux choses consiste à utiliser quelque chose comme la notation scientifique. Nous sommes habitués à voir des nombres exprimés en notation scientifique comme 6.022141 * 10 ^ 23. Les nombres en virgule flottante sont stockés en interne en utilisant un format similaire - mantisse et exposant, mais en utilisant des puissances de deux au lieu de dix.
Votre 61.0 pourrait être réécrit sous la forme 1.90625 * 2 ^ 5 ou 1.11101b * 2 ^ 101b avec la mantisse et les exposants. Pour multiplier cela par dix et (déplacer le point décimal), on peut faire:
(1.90625 * 2 ^ 5) * (1.25 * 2 ^ 3) = (2.3828125 * 2 ^ 8) = (1.19140625 * 2 ^ 9)
ou avec la mantisse et les exposants en binaire:
(1.11101b * 2 ^ 101b) * (1.01b * 2 ^ 11b) = (10.0110001b * 2 ^ 1000b) = (1,00110001b * 2 ^ 1001b)
Notez ce que nous avons fait pour multiplier les nombres. Nous avons multiplié les mantisses et ajouté les exposants. Ensuite, comme la mantisse s’est terminée à plus de deux, nous avons normalisé le résultat en heurtant l’exposant. C'est comme si nous ajustions l'exposant après une opération sur les nombres en notation scientifique décimale. Dans chaque cas, les valeurs avec lesquelles nous avons travaillé avaient une représentation finie en binaire. Par conséquent, les valeurs générées par les opérations de multiplication et d'addition de base produisaient également des valeurs avec une représentation finie.
Maintenant, réfléchissez à la manière dont nous diviserions 61 par 10. Nous commencerions par diviser les mantisses 1.90625 et 1.25. En décimal, cela donne 1,525, un nombre court de Nice. Mais qu'en est-il si nous le convertissons en binaire? Nous le ferons comme d'habitude - en soustrayant autant que possible la plus grande puissance de deux, comme pour convertir des nombres décimaux en entiers en binaire, mais nous utiliserons des puissances négatives de deux:
1.525 - 1 * 2 ^ 0 -> 1 0,525 - 1 * 2 ^ -1 -> 1 0,025 - 0 * 2 ^ -2 -> 0 0.025 - 0 * 2 ^ -3 -> 0 0.025 - 0 * 2 ^ -4 -> 0 0.025 - 0 * 2 ^ -5 -> 0 0.025 - 1 * 2 ^ -6 -> 1 0.009375 - 1 * 2 ^ -7 -> 1 0.0015625 - 0 * 2 ^ -8 - > 0 0.0015625 - 0 * 2 ^ -9 -> 0 0.0015625 - 1 * 2 ^ -10 -> 1 0.0005859375 - 1 * 2 ^ -11 - -> 1 0.00009765625 ...
Euh oh. Maintenant nous avons des problèmes. Il s’avère que 1.90625/1.25 = 1.525 est une fraction répétitive exprimée en binaire: 1.11101b/1.01b = 1.10000110011 ... b Nos machines n’ont que très peu de bits pour contenir cette mantisse et c’est donc juste autour de la fraction. et assumer des zéros au-delà d'un certain point. L'erreur que vous voyez lorsque vous divisez 61 par 10 est la différence entre:
1.1000011001100110011001100110011001100110011 ... b * 2 ^ 10b
et dis:
1.100001100110011001100110b * 2 ^ 10b
C'est cet arrondi de la mantisse qui conduit à la perte de précision que nous associons aux valeurs à virgule flottante. Même lorsque la mantisse peut être exprimée avec précision (par exemple, lors de l’ajout de deux nombres), nous pouvons toujours subir une perte numérique si la mantisse a besoin de trop de chiffres pour tenir après la normalisation de l’exposant.
En fait, nous faisons ce genre de chose tout le temps lorsque nous arrondissons les nombres décimaux à une taille gérable et que nous n'en donnons que les premiers chiffres. Parce que nous exprimons le résultat en décimal, cela semble naturel. Mais si nous arrondissons une décimale puis la convertissons en une base différente, elle aura l’air aussi moche que la décimale obtenue en raison de l’arrondi en virgule flottante.
C'est une bonne question.
Toute votre question est basée sur "comment représente-t-on un nombre?"
TOUS les nombres peuvent être représentés avec une représentation décimale ou avec une représentation binaire (complément à 2). tous !!
MAIS certains (la plupart d'entre eux) nécessitent un nombre infini d'éléments ("0" ou "1" pour la position binaire, ou "0", "1" à "9" pour la représentation décimale ).
Comme 1/3 en représentation décimale (1/3 = 0.3333333 ... <- avec un nombre infini de "3")
Comme 0.1 en binaire (0.1 = 0.00011001100110011 .... <- avec un nombre infini de "0011")
Tout est dans ce concept. Puisque votre ordinateur ne peut prendre en compte que fini ensemble de chiffres (décimal ou binaire), seuls certains nombres peuvent être représentés exactement dans votre ordinateur ...
Et comme dit Jon, 3 est un nombre premier qui n'est pas un facteur de 10, donc 1/3 ne peut pas être représenté avec un nombre fini d'éléments en base 10.
Même avec une arithmétique avec une précision arbitraire, le système de position de numérotation en base 2 n'est pas en mesure de décrire complètement 6.1, bien qu'il puisse représenter 61.
Pour 6.1, nous devons utiliser une autre représentation (comme la représentation décimale ou IEEE 854 qui autorise la base 2 ou la base 10 pour la représentation des valeurs à virgule flottante).
Je suis surpris que personne ne l'ait dit pour le moment: utilisez fractions continues . Tout nombre rationnel peut être représenté finement en binaire de cette façon.
Quelques exemples:
1/3 (0,3333 ...)
0; 3
5/9 (0,5555 ...)
0; 1, 1, 4
10/43 (0.232558139534883720930 ...)
0; 4, 3, 3
9093/18478 (0.49209871198181621387596060179673 ...)
0; 2, 31, 7, 8, 5
À partir de là, il existe une variété de moyens connus pour stocker une séquence d'entiers en mémoire.
En plus de stocker votre numéro avec une précision parfaite, les fractions continues présentent également d'autres avantages, tels qu'une meilleure approximation rationnelle. Si vous décidez de terminer la séquence de chiffres d'une fraction continue plus tôt, les chiffres restants (une fois recombinés) vous donneront la meilleure fraction possible. Voici comment les approximations de pi sont trouvées:
La fraction continue de Pi:
3; 7, 15, 1, 292 ...
En terminant la séquence à 1, cela donne la fraction:
355/113
ce qui est une excellente approximation rationnelle.
Dans l'équation
2^x = y ;
x = log(y) / log(2)
Par conséquent, je me demandais si nous pourrions avoir un système de base logarithmique pour les binaires comme,
2^1, 2^0, 2^(log(1/2) / log(2)), 2^(log(1/4) / log(2)), 2^(log(1/8) / log(2)),2^(log(1/16) / log(2)) ........
Cela pourrait peut-être résoudre le problème, donc si vous voulez écrire quelque chose comme 32.41 en binaire, ce serait
2^5 + 2^(log(0.4) / log(2)) + 2^(log(0.01) / log(2))
Ou
2^5 + 2^(log(0.41) / log(2))
Le problème est que vous ne savez pas vraiment si le nombre est exactement 61,0. Considère ceci:
float a = 60;
float b = 0.1;
float c = a + b * 10;
Quelle est la valeur de c? Ce n'est pas exactement 61, car b n'est pas vraiment .1 car .1 n'a pas une représentation binaire exacte.
Il y a un seuil parce que la signification du chiffre est passée d'entier à non entier. Pour représenter 61, vous avez 6 * 10 ^ 1 + 1 * 10 ^ 0; 10 ^ 1 et 10 ^ 0 sont tous deux des entiers. 6.1 est 6 * 10 ^ 0 + 1 * 10 ^ -1, mais 10 ^ -1 est 1/10, ce qui n'est certainement pas un entier. C'est comme ça que vous vous retrouvez à Inexactville.
Un parallèle peut être constitué de fractions et de nombres entiers. Certaines fractions, par exemple 1/7, ne peuvent pas être représentées sous forme décimale sans beaucoup de nombres décimaux. La virgule flottante étant binaire, les cas particuliers changent, mais le même type de problèmes de précision se pose.
Comme nous l'avons vu, en arithmétique en virgule flottante, la décimale 0,1 ne peut pas être parfaitement représentée en binaire.
Les représentations en virgule flottante et en nombre entier fournissent des grilles ou des réseaux pour les nombres représentés. Au fur et à mesure que l'arithmétique est terminée, les résultats sont retirés de la grille et doivent être replacés sur la grille en arrondissant. Exemple: 1/10 sur une grille binaire.
Si nous utilisons la représentation décimale codée binaire, comme le suggère un monsieur, serions-nous en mesure de conserver les nombres sur la grille?
Le nombre 61.0 a effectivement une opération en virgule flottante exacte, mais ce n'est pas vrai pour les entiers tous. Si vous écrivez une boucle qui en ajoute un à la fois à un nombre à virgule flottante double précision et à un entier de 64 bits, vous atteindrez éventuellement un point où l'entier de 64 bits représente parfaitement un nombre, mais le point de virgule flottante ne ... parce qu'il n'y a pas assez de bits significatifs.
Il est tout simplement beaucoup plus facile d’atteindre le point d’approximation situé à droite du point décimal. Si vous commenciez à écrire tous les nombres en virgule flottante binaire, cela aurait plus de sens.
Une autre façon de penser est que, lorsque vous remarquez que 61.0 est parfaitement représentable en base 10, et que vous ne modifiez pas le point décimal, vous effectuez une multiplication par dix (10 ^ 1, 10 ^ -1 ). En virgule flottante, la multiplication par des puissances de deux n'affecte pas la précision du nombre. Essayez de prendre 61.0 et de le diviser par trois à plusieurs reprises pour illustrer comment un nombre parfaitement précis peut perdre sa représentation précise.
Il existe un nombre infini de nombres rationnels et un nombre fini de bits permettant de les représenter. Voir http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems .
tu connais des nombres entiers non? chaque bit représente 2 ^ n
2 ^ 4 = 16
2 ^ 3 = 8
2 ^ 2 = 4
2 ^ 1 = 2
2 ^ 0 = 1
c'est la même chose pour la virgule flottante (avec quelques distinctions) mais les bits représentent 2 ^ -n 2 ^ -1 = 1/2 = 0,5
2 ^ -2 = 1/(2 * 2) = 0,25
2 ^ -3 = 0,125
2 ^ -4 = 0.0625
Représentation binaire à virgule flottante:
signer la fraction de l'exposant (je pense que l'invisible 1 est ajouté à la fraction)
B11 B10 B9 B8 B7 B6 B5 B4 B3 B2 B1 B0
La réponse de haut score ci-dessus l'a cloué.
Vous avez d’abord mélangé la base 2 et la base 10 dans votre question, puis lorsque vous avez placé un chiffre du côté droit qui n’est pas divisible dans la base, vous obtenez des problèmes. Comme 1/3 en décimal car 3 ne va pas dans une puissance de 10 ou 1/5 en binaire qui ne va pas dans une puissance de 2.
Autre commentaire, n'utilisez JAMAIS l'égalité avec les nombres à virgule flottante, point. Même s'il s'agit d'une représentation exacte, il existe dans certains systèmes des nombres qui peuvent être représentés avec précision de plusieurs façons (IEEE est mauvais à ce sujet, il s'agit d'une horrible spécification pour commencer, attendez-vous à des maux de tête). Pas différent ici 1/3 n'est pas égal au nombre sur votre calculatrice 0.3333333, peu importe combien de 3 il y a à droite du point décimal. C'est ou peut être assez proche mais ce n'est pas égal. vous vous attendez donc à ce que quelque chose comme 2 * 1/3 ne soit pas égal à 2/3 en fonction de l'arrondi. Ne jamais utiliser égal avec virgule flottante.