49 votes

Pourquoi une variable flottante cesse-t-elle de s'incrémenter à 16777216 en C# ?

float a = 0;
while (true)
{
    a++;
    if (a > 16777216)
        break; // Will never break... a stops at 16777216
}

Quelqu'un peut-il m'expliquer pourquoi une valeur flottante cesse de s'incrémenter à 16777216 dans ce code ?

Edita:

Ou encore plus simple :

float a = 16777217; // a becomes 16777216

56voto

AndiDog Points 28417

Petit récapitulatif des nombres à virgule flottante IEEE-754 (32 bits) que j'ai en tête :

  • 1 bit de signe (0 signifie un nombre positif, 1 signifie un nombre négatif)
  • Exposant 8 bits (avec un biais de -127, pas important ici)
  • 23 bits "mantisse"
  • À l'exception des valeurs d'exposant 0 et 255, vous pouvez calculer la valeur comme suit : (sign ? -1 : +1) * 2^exponent * (1.0 + mantissa)
    • Les bits de la mantisse représentent binaire chiffres après le séparateur décimal, par exemple 1001 0000 0000 0000 0000 000 = 2^-1 + 2^-4 = .5 + .0625 = .5625 et la valeur devant le séparateur décimal n'est pas stockée mais implicitement assumée comme 1 (si l'exposant est 255, 0 est assumé mais ce n'est pas important ici), donc pour un exposant de 30, par exemple, cet exemple de mantisse représente la valeur 1.5625

Revenons à votre exemple :

16777216 est exactement 2 24 et serait représenté sous forme de flottant 32 bits comme suit :

  • signe = 0 (nombre positif)
  • exposant = 24 (stocké comme 24+127=151=) 10010111 )
  • mantisse = .0
  • En tant que représentation à virgule flottante de 32 bits : 0 10010111 00000000000000000000000
  • Par conséquent : Valeur = (+1) * 2^24 * (1.0 + .0) = 2^24 = 16777216

Maintenant, regardons le nombre 16777217, ou exactement 2 24 +1 :

  • le signe et l'exposant sont les mêmes
  • la mantisse devrait être exactement 2 -24 de sorte que (+1) * 2^24 * (1.0 + 2^-24) = 2^24 + 1 = 16777217
  • Et voilà le problème. La mantisse ne peut pas avoir la valeur 2 -24 parce qu'il n'a que 23 bits, donc le nombre 16777217 ne peut tout simplement pas être représenté avec la précision des nombres à virgule flottante de 32 bits !

14voto

Eric J. Points 73338

16777217 ne peut pas être représenté exactement par un float. Le prochain nombre le plus élevé qu'un flottant peut représenter exactement est 16777218.

Vous essayez donc d'incrémenter la valeur flottante 16777216 à 16777217, qui ne peut pas être représentée dans un flottant.

13voto

O. R. Mapper Points 9163

Si vous regardez cette valeur dans sa représentation binaire, vous verrez qu'il s'agit d'un un et de nombreux zéros, à savoir 1 0000 0000 0000 0000 0000 0000 ou exactement 2^24. Cela signifie que, à 16777216, le nombre vient de croître d'un chiffre.

Comme il s'agit d'un nombre à virgule flottante, cela peut signifier que le dernier chiffre à sa fin qui est encore stocké (c'est-à-dire dans sa précision) est également décalé vers la gauche.

Probablement, ce que vous voyez, c'est que le dernier chiffre de la précision est passé à quelque chose de plus grand que 1, de sorte que l'ajout de 1 ne fait plus aucune différence.

2voto

Max Points 21

Imaginez ceci sous forme décimale. Supposons que vous ayez le nombre :

1.000000 * 10^6

ou 1.000.000. Si vous n'aviez que six chiffres de précision, en ajoutant 0,5 à ce nombre, vous obtiendriez

1.0000005 * 10^6

Cependant, l'idée actuelle concernant les modes d'arrondi de fp est d'utiliser "Arrondir au pair" plutôt que "Arrondir au plus proche". Dans ce cas, chaque fois que vous incrémentez cette valeur, elle sera arrondie dans l'unité à virgule flottante à 16 777 216, ou 2^24. Les singles en IEE 754 sont représentés par :

+/- exponent (1.) fraction

où le "1." est implicite et la fraction est de 23 bits supplémentaires, tous des zéros, dans ce cas. Le 1 binaire supplémentaire se répandra dans le chiffre de garde, se reportera sur l'étape d'arrondi et sera supprimé à chaque fois, quel que soit le nombre de fois où vous l'incrémentez. Le site ulp o unité en dernière position sera toujours égal à zéro. Le dernier incrément réussi est de :

+2^23 * (+1.) 11111111111111111111111 -> +2^24 * (1.) 00000000000000000000000

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X