Question

Le code suivant en C # (.Net 3.5 SP1) est une boucle infinie sur ma machine:

for (float i = 0; i < float.MaxValue; i++) ;

Il a atteint le nombre 16.777.216,0 et 16.777.216,0 + 1 est évalué à 16.777.216,0. Pourtant, à ce moment:. I + 1 = i

Ceci est une folie.

Je sais qu'il ya une inexactitude dans la façon dont les nombres à virgule flottante sont stockés. Et je l'ai lu que des nombres entiers plus 2 ^ 24 que ne peut pas être correctement stocké sous forme d'un flotteur.

Toujours le code ci-dessus, doit être valide en C #, même si le nombre ne peut pas être correctement représentée.

Pourquoi ça ne fonctionne pas?

Vous pouvez obtenir le même arriver pour le double mais il faut un temps très long. 9007199254740992,0 est la limite pour le double.

Était-ce utile?

La solution

Bon, alors la question est que pour ajouter un au flotteur, il devrait devenir

16777217.0

Il se trouve que ce soit à une limite pour la radix et ne peut pas être représenté exactement comme un flotteur. (La prochaine valeur la plus élevée disponible est 16777218.0)

Alors, il arrondit au flotteur représentable le plus proche

16777216.0

Permettez-moi de cette façon:

Puisque vous avez un flottant quantité de précision, vous devez augmenter par un nombre plus élevé et supérieur.

EDIT:

Ok, cela est un peu difficile à expliquer, mais essayez ceci:

float f = float.MaxValue;
f -= 1.0f;
Debug.Assert(f == float.MaxValue);

fonctionnera très bien, parce qu'à cette valeur, afin de représenter une différence de 1.0f, vous auriez besoin de plus de 128 bits de précision. Un flotteur a seulement 32 bits.

EDIT2

D'après mes calculs, au moins 128 chiffres binaires non signé serait nécessaire.

log(3.40282347E+38) * log(10) / log(2) = 128

En tant que solution à votre problème, vous pourriez créer une boucle à travers deux nombres de 128 bits. Cependant, cela prendra au moins dix ans pour compléter.

Autres conseils

Imaginez par exemple qu'un nombre à virgule flottante est représenté par jusqu'à 2 chiffres décimaux significatifs, plus un exposant: dans ce cas, vous pouvez compter de 0 à 99 exactement. La prochaine serait 100, mais parce que vous ne pouvez avoir 2 chiffres significatifs qui seraient stockés comme « 1,0 fois 10 à la puissance de 2 ». Ajout d'un à qui serait ... quoi?

Au mieux, ce serait 101 comme un résultat intermédiaire, qui serait effectivement stocké (par une erreur d'arrondi qui rejette le 3ème chiffre insignifiant) comme « 1,0 fois 10 à la puissance de 2 » à nouveau.

Pour comprendre ce qui se passe mal, vous allez devoir lire la norme IEEE sur virgule flottante

Examinons la structure d'un virgule flottante numéro pour une seconde:

Un nombre à virgule flottante est divisé en deux parties (3 ok, mais ignore le bit de signe pour une seconde).

Vous avez un exposant et une mantisse. Comme ceci:

smmmmmmmmeeeeeee

Note:. Qui ne acurate au nombre de bits, mais il vous donne une idée générale de ce qui se passe

Pour savoir quel numéro vous nous faites le calcul suivant:

mmmmmm * 2^(eeeeee) * (-1)^s

Alors qu'est-ce float.MaxValue va être? Eh bien, vous allez avoir le plus grand mantisse possible et le plus grand exposant possible. Feignons cela ressemble quelque chose comme:

01111111111111111

dans la réalité, nous définissons NAN et + -INF et deux autres conventions, mais les ignorer pour une seconde, car ils ne sont pas pertinents à votre question.

Alors, qu'est-ce qui se passe quand vous avez 9.9999*2^99 + 1? Eh bien, vous n'avez pas assez de chiffres significatifs pour ajouter 1. En conséquence, il s'arrondi au même nombre. Dans le cas de précision à virgule flottante point où +1 commence à s'arrondi arrive à 16777216.0

Il n'a rien à voir avec le débordement, ou d'être proche de la valeur max. La valeur de flotteur pour 16.777.216,0 a une représentation binaire de 16777216. Vous pouvez ensuite incrémentons 1, il devrait donc être 16.777.217,0, sauf que la représentation binaire de 16.777.217,0 est 16777216 !!! Donc, il ne se fait pas incrémenté ou au moins l'incrément ne fait pas ce que vous attendez.

Voici une classe écrite par Jon Skeet qui illustre ceci:

DoubleConverter.cs

Essayez ce code avec lui:

double d1 = 16777217.0;
Console.WriteLine(DoubleConverter.ToExactString(d1));

float f1 = 16777216.0f;
Console.WriteLine(DoubleConverter.ToExactString(f1));

float f2 = 16777217.0f;
Console.WriteLine(DoubleConverter.ToExactString(f2));

Remarquez comment la représentation interne de 16.777.216,0 est le même 16.777.217,0 !!

L'itération quand j'approche float.MaxValue a i juste en dessous de cette valeur. L'itération suivante ajoute à i, mais il ne peut pas contenir un nombre plus grand que float.MaxValue. Ainsi, il est titulaire d'une valeur beaucoup plus petite, et commence la boucle à nouveau.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top