La boucle While ne se termine pas – arithmétique flottante

Je travaille sur le programme C avec des valeurs flottantes, voici mon code.

#include #include #include int main() { int counter = 0; float quarter = 0.25; float dime = 0.10; float nickel = 0.05; float penny = 0.01; float change = 0.00; printf("hi, how much do i owe u?\t"); scanf("%f", &change); while(change > 0.0) { if(change >= quarter) { change -= quarter; printf("quarter %.2f\n", quarter); } else if(change >= dime) { change -= dime; printf("dime %.2f\n", dime); } else if(change >= nickel) { change -= nickel; printf("nickel %.2f\n", nickel); } else if(change >= penny) { change -= penny; printf("penny %.2f\n", penny); } counter++; } printf("your count is %i\n", counter); return 0; } 

La sortie est:

 hi, how much do i owe u? .45 quarter 0.25 dime 0.10 nickel 0.05 penny 0.01 penny 0.01 penny 0.01 penny 0.01 `^C` 

Je dois appuyer sur ctrl c pour terminer la boucle

Dernière printf("your count is %i\n", counter); ne s’exécute pas du tout – count # of coins utilisées

Si je remplace le type float par int cela fonctionne bien.

S’il vous plaît aider avec ce problème

Ajoutant ceci au début de votre boucle:

 if (change < penny) { printf("remaining: %.10f\n", change); break; } 

Émettra cette sortie:

 hi, how much do i owe u? .45 quarter 0.25 dime 0.10 nickel 0.05 penny 0.01 penny 0.01 penny 0.01 penny 0.01 remaining: 0.0099999849 your count is 7 

Ceci est dû à la représentation interne des nombres flottants. Il y a une erreur insortingnsèque - la plupart des nombres flottants n'ont pas une représentation exacte - ils sont juste très bons approximations.

Faites-vous une énorme faveur. Prenez la valeur entrée, multipliez-la par 100 et effectuez tous vos calculs en centimes plutôt qu’en dollars. Seules vos sorties imprimées doivent être converties en dollars et en cents.

Les ordinateurs sont beaucoup plus précis avec les entiers qu’avec les flottants.

Imprimez le change à la fin de votre boucle et voyez ce qu’il devient à la toute fin. Je suppose que ce sera un très petit nombre positif en raison d’erreurs en virgule flottante. Vous pouvez résoudre ce problème en changeant simplement votre instruction while en change > 1.e-3 ou en un autre nombre plus petit.

Le problème avec votre boucle est qu’il n’est possible de faire correspondre aucune des affaires. Moi-même, j’écrirais la dernière twig de la chaîne des ifs comme

 else { /* must be the case that change >= penny */ change -= penny; printf("penny %.2f\n", penny); } 

Le commentaire explique ce qui devrait être le cas ici, mais le programme n’échouera pas s’il n’est pas vrai.

Si pour une raison quelconque vous deviez vous assurer que ce commentaire était vrai (ce n’est peut-être pas un problème, mais dans un contexte à enjeux plus importants, cela pourrait être le cas), vous pouvez écrire soit

 else { assert(change >= penny); change -= penny; ... } 

ou si vous vouliez conserver la structure que vous avez, vous pouvez écrire

 else if(change >= penny) { change -= penny; printf("penny %.2f\n", penny); } else { /* ooops! This should be impossible */ fprintf(stderr, "Can't happen: change %f < penny\n", change); exit(1); } 

ou quelque chose comme ça. Avec une chaîne de déclarations if comme celle-là sans un fourre-tout, ou avec un switch without a défaut`, vous devriez prendre l'habitude de penser automatiquement "que se passe-t-il si l'impossible se produit?"

C'est un problème plus vaste (et peut-être un peu déroutant pour le moment), mais la programmation avec assertions est une très bonne habitude à prendre. Si vous pouvez vous convaincre qu'il "est impossible que X soit faux" (tel que le pointer != NULL ou i < i_max ), écrivez cela comme une affirmation. Si cette assertion est fausse, vous avez immédiatement constaté une erreur de logique majeure dans votre code.

Dernière chose: pourquoi ce dernier if test échoue? En arithmétique mathématique, cela ne peut pas échouer, mais souvenez-vous que les ordinateurs utilisent des nombres à virgule flottante, dans lesquels 0,01 n'est pas représentable avec précision. Ainsi, le nombre représenté par 0.01 (ou 0.05 ou 0.10) n’est pas réellement cela, mais un nombre situé à environ 1e-7, ce qui signifie que la valeur de change que vous avez sera quelque chose soit juste en dessous de 0.01, soit juste au - dessus de 0.0 et c’est pourquoi le change > 0.0 peut être vrai et le change >= 0.01 faux.