La variable float ne remplit pas les conditions (C)

J’essaie d’amener l’utilisateur à entrer un nombre compris entre 1.00000 et 0.00001 alors que les arêtes ne sont pas incluses dans une variable float Je peux supposer que l’utilisateur ne tape pas plus de 5 chiffres après le point. maintenant, voici ce que j’ai écrit:

printf("Enter required Leibniz gap.(Between 0.00001 to 1.00000)\n"); scanf("%f", &gap); while ((gap  0.99999)) { printf("Enter required Leibniz gap.(Between 0.00001 to 1.00000)\n"); scanf("%f", &gap); } 

maintenant, quand je tape le plus petit nombre possible: 0.00002 pour restr bloqué dans la boucle while. Lorsque j’exécute le débogueur, j’ai constaté que 0.00002 est stocké avec cette valeur dans la variable float: 1.99999995e-005. Toute personne peut m’expliquer ce que je ne fais pas bien. pourquoi 0.00002 ne remplit-il pas les conditions? quelle est cette chose “1.99999995e-005”.

float s ne sont pas capables de stocker des valeurs exactes pour chaque nombre possible (nombres infinis compris entre 0 et 1 donc impossible). L’atsortingbution de 0.00002 à un float aura un numéro différent mais vraiment proche en raison de l’implémentation que vous rencontrez. La précision diminue à mesure que le nombre augmente.

Vous ne pouvez donc pas comparer directement deux flotteurs proches et obtenir des résultats sains.

Plus d’informations sur les points flottants peuvent être trouvées sur cette page Wikipedia .

Ce que vous pourriez faire, c’est émuler des mathématiques à points fixes. Avoir un int n = 100000; pour représenter 1.00000 interne (1000 -> 0.001 et tel) et faire des calculs en conséquence ou utiliser une bibliothèque mathématique à point fixe.

Le problème ici est que vous utilisez une variable float ( gap ), mais que vous la comparez à une double constante ( 0.00002 ). La constante est double parce que les constantes à virgule flottante dans C sont doubles sauf indication contraire.

Un problème sous-jacent est que le nombre 0.00002 ne peut être représenté ni float ni double . (Ce n’est pas du tout représentable en virgule flottante binary car son développement binary est infiniment long, comme le développement décimal de.) Ainsi, lorsque vous écrivez 0.00002 dans un programme, le compilateur C le remplace par une valeur double très proche de 0.00002 . De même, lorsque scanf lit le nombre 0.00002 dans une variable float , il substitue une valeur float très proche de 0.00002 . Étant donné que double nombres double ont plus de bits que les nombres floats , la valeur double est plus proche de 0.00002 que la valeur float .

Lorsque vous comparez deux valeurs en virgule flottante avec une précision différente, le compilateur convertit la valeur avec moins de précision en exactement la même valeur avec plus de précision. (L’ensemble de valeurs représentable en tant que double est un sur-ensemble de l’ensemble de valeurs représentable en tant que float , il est donc toujours possible de trouver un double dont la valeur est identique à la valeur d’un float .) Et c’est ce qui se produit lorsque gap < 0.00002 est exécuté: gap est converti en un double de la même valeur et est comparé au double (proche de) 0.00002 . Étant donné que ces deux valeurs sont en réalité légèrement inférieures à 0,00002 et que le double est plus proche, le float est inférieur au double .

Vous pouvez résoudre ce problème de plusieurs manières. Premièrement, vous pouvez éviter la conversion, soit en double gap et en modifiant le format scanf en %lf , soit en comparant gap à un float :

 while (gap < 0.00002F || gap > 0.99999F) { 

Mais ce n'est pas vraiment correct, pour deux raisons. Premièrement, rien ne garantit en réalité que la conversion en virgule flottante effectuée par le compilateur C est identique à celle effectuée par la bibliothèque standard ( scanf ), et la norme permet au compilateur d’utiliser "la valeur représentable la plus proche, ou la valeur la plus grande." ou une valeur représentable inférieure, immédiatement adjacente à la valeur représentable la plus proche, choisie selon une définition définie par la mise en oeuvre. " (Il ne spécifie pas non plus en détail quelle valeur scanf produit, mais recommande que ce soit la valeur représentable la plus proche.) En l'occurrence, gcc et glibc (le compilateur C et la bibliothèque standard utilisée sous Linux) génèrent tous deux la valeur représentable la plus proche, mais d'autres implémentations ne le font pas.

Quoi qu'il en soit, selon votre message d'erreur, vous souhaitez que la valeur soit comprise entre 0.00001 et 1.00000 . Donc, votre test devrait être précisément cela:

 while (gap <= 0.00001F || gap >= 1.0000F) { ... 

(en supposant que vous gardiez l' gap comme un float .)

N'importe laquelle des solutions ci-dessus fonctionnera. Personnellement, je ferais un double un gap afin de rendre la comparaison plus intuitive, et changerais également la comparaison pour la comparer à 0.00001 et 1.0000 .

Soit dit en passant, le suffixe E-05 signifie "dix fois la puissance de -5" ( E signifie Exponent ). Vous verrez cela souvent; c'est un moyen standard d'écrire des constantes à virgule flottante.

La partie fraction des nombres flottants en simple précision peut représenter des nombres compris entre -2 et 2-2 ^ -23 et avoir une partie fraction avec le plus petit pas de quantification de 2 ^ -23. Donc, si une valeur ne peut pas être représentée avec une telle étape, elle est représentée avec une valeur la plus proche conformément aux règles d’arrondi IEEE 754 :

 0.00002*32768 = 0.655360043 // floating point exponent is chosen. 0.655360043/(2^-23) = 5497558.5 // is not an integer multiplier // of quantization step, so the 5497558*(2^-23) = 0.655359983 // nearest value is chosen 5497559*(2^-23) = 0.655360103 // from these two variants 

La première variante correspond à 1.999969797 × 10⁻⁵ en format décimal et la seconde à 1.999999948 × 10⁻⁵ (juste pour comparer – si nous choisissons 5497560, nous obtenons 2.000000677 × 10⁻⁵). La deuxième variante peut donc être choisie et sa valeur n’est pas égale à 0,00002.
La précision totale du nombre à virgule flottante dépend également de la valeur de l’exposant (valeurs comsockets entre -128 et 127): elle peut être calculée par multiplication du pas de quantification de la fraction et de la valeur de l’exposant. Dans le cas de 0.00002, la précision totale est de (2 ^ -23) × (2 ^ -15) = 3,6 × (10 ^ -12). Cela signifie que si nous ajoutons à 0.00002, une valeur inférieure à la moitié de cette valeur est égale à 0.00002. En général, cela signifie que les nombres de nombres en virgule flottante qui ont un sens vont de 1 × exposant à 2 × (10 ^ -23) × exposant.
C’est pourquoi une approche très populaire consiste à comparer deux nombres flottants en utilisant une valeur epsilon supérieure à l’étape de quantification.

Comme certains commentaires l’ont dit, en raison de la façon dont les nombres en virgule flottante sont représentés, vous verrez des erreurs comme celle-ci. Une solution à cela est de le convertir en

 gap + 1e-8 < 0.0002 

Cela vous donne une petite fenêtre de tolérance suffisante pour laisser passer la plupart des cas et ne pas vouloir échouer