Les nombres à virgule flottante sont représentés en interne comme un nombre binaire, presque toujours au format IEEE. Vous pouvez voir comment les nombres sont représentés ici :
http://babbage.cs.qc.edu/IEEE-754/
Par exemple, 0,25 en binaire correspond à 0,01. b et est représenté par +1.00000000000000000000000 * 2 -2 .
Il est stocké en interne avec 1 bit pour le signe, 8 bits pour l'exposant (qui représente une valeur comprise entre -127 et +128, et 23 bits pour la valeur (le 1 de tête n'est pas stocké). En fait, les bits sont :
[0][01111101][00000000000000000000000]
Alors que 0,2 en binaire n'a pas de représentation exacte, tout comme 1/3 n'a pas de représentation exacte en décimal.
Ici, le problème est que, de même que 1/2 peut être représenté exactement en format décimal comme 0,5, mais 1/3 ne peut être qu'approximé à 0,3333333333, 0,25 peut être représenté exactement comme une fraction binaire, mais pas 0,2. En binaire, c'est 0,0010011001100110011001100..... b où les quatre derniers chiffres se répètent.
Pour être stocké sur un ordinateur, il est évalué à 0,001001100110011001100110101. b . Ce qui est très, très proche, donc si vous calculez des coordonnées ou quoi que ce soit d'autre où les valeurs absolues comptent, c'est parfait.
Malheureusement, si vous ajoutez cette valeur à elle-même cinq fois, vous obtiendrez 1,00000000000000000000001. b . (Ou, si vous aviez arrondi 0,2 à 0,00100110011001100110011001100 b au lieu de cela, vous obtiendriez 0,11111111111111111111100 b )
De toute façon, si votre condition de boucle est 1.00000000000000000000001 b \==1.00000000000000000000000 b il ne se terminera pas. Si vous utilisez <= à la place, il est possible qu'il s'exécute une fois de plus si la valeur est juste inférieure à la dernière valeur, mais il s'arrêtera.
Il serait possible de créer un format capable de représenter avec précision les petites valeurs décimales (comme toute valeur ne comportant que deux décimales). Elles sont utilisées dans les calculs financiers, etc. Mais les valeurs normales à virgule flottante fonctionnent de la même manière : elles échangent la capacité de représenter quelques petits nombres "faciles" comme 0,2 contre la capacité de représenter une large gamme de manière cohérente.
Il est courant d'éviter d'utiliser un flottant comme compteur de boucle pour cette raison précise, les solutions courantes seraient :
- Si une itération supplémentaire n'a pas d'importance, utilisez <=
- Si cela a de l'importance, créez la condition <=1.0001 à la place, ou une autre valeur inférieure à votre incrément, de sorte que les erreurs de 0.0000000000000000000001 n'aient pas d'importance.
- Utiliser un nombre entier et le diviser par quelque chose pendant la boucle.
- Utilisez une classe spécialement conçue pour représenter exactement les valeurs fractionnaires.
Il serait possible pour un compilateur d'optimiser une boucle float "=" pour la transformer en ce que vous souhaitez, mais je ne sais pas si cela est autorisé par la norme ou si cela se produit dans la pratique.