2010-09-02 14 views
1

Je compilé le programme suivant avec gcc 4.4.1 et j'obtenir une sortie inattendue (bien, inattendu pour moi)Bloc If-else et résultats inattendus avec le type de données float. [Edité avec une question]

#include<stdio.h> 

int main() 
{ 
     float x=0.3, y=0.7; 

     if(x==0.3) 
     { 
       if(y==0.7) 
         printf("Y\n\n"); 
       else 
         printf("X\n\n"); 
     } 
     else 
       printf("NONE\n\n"); 
} 


Output: NONE 

#include<stdio.h> 

int main() 
{ 
     float x=0.3, y=0.7; 

     if(x<0.3) 
     { 
       if(y==0.7) 
         printf("Y\n\n"); 
       else 
         printf("X\n\n"); 
     } 
     else 
       printf("NONE\n\n"); 
} 

Output: NONE 

#include<stdio.h> 

int main() 
{ 
     float x=0.3, y=0.7; 

     if(x>0.3) 
     { 
       if(y>0.7) 
         printf("Y\n\n"); 
       else 
         printf("X\n\n"); 
     } 
     else 
       printf("NONE\n\n"); 
} 

    Output:X 

Donc, il est clairement visible que la valeur stockée dans "x" est supérieure à 0,3 et la valeur stockée dans "y" est inférieure à 0,7

Pourquoi cela se passe-t-il? Est-ce une propriété du type de données float ou les instructions if-else interprètent float d'une manière différente?

Merci.


Editer: D'accord, j'ai réfléchi et je commence à avoir un peu de confusion maintenant. Veuillez dire si ma compréhension de ce problème est correcte ou non.

float x=0.3; 

Ceci stocke x=0.30000001192092895508 dans la mémoire. De toute évidence, cela est supérieur à 0.3 (Est-ce correct?)

Maintenant, double x=0.3 résultats dans x=0.29999999999999998890 ce qui est inférieur à 0,3

question principale (Est-ce exact aussi?): Donc, si j'utilise magasin 0.3 dans float x, l'instruction suivante if(x>0.3) donne x=0.30000001192092895508 étant implicitement castée en tant que double et 0.3 est également un double au lieu d'un flottant. Par conséquent 0.3=0.29999999999999998890 et l'opération interne est if((double) 0.30000001192092895508 > (double) 0.29999999999999998890). Est-ce correct?

+2

Veuillez ajouter "f" aux valeurs du flotteur et afficher les résultats. Si Java le sait, les nombres flottants non définis explicitement avec "f" comme flottant obtiendront un double. Probablement des choses étranges se produisent lors de la coulée entre le flotteur et le double. Il se pourrait que 0.3 devienne 0.30000000000000001 en raison d'une erreur d'arrondi. –

+1

Mieux encore, changez toutes vos variables en «double». Toutes les fonctions mathématiques de C renvoient 'double ', donc vous pourriez aussi bien être cohérent. 'float' est seulement utile pour sauver la mémoire quand vous avez un tableau de millions d'entre eux. – dan04

Répondre

7

Vous utilisez float pour le stockage, mais vous effectuez des comparaisons avec les littéraux de type double.

Les valeurs x et y ne sont pas exactement 0,3 et 0.7, car ces nombres ne sont pas représentables en virgule flottante binaire. Il arrive que le float le plus proche de 0.3 soit plus grand que le double le plus proche à 0.3, et le float le plus proche à 0.7 soit inférieur au double le plus proche à 0.7 ... d'où vos résultats de comparaison.

En supposant que les représentations sont les mêmes que dans C# (où j'arrive d'avoir des outils pour aider) les valeurs impliquées sont:

0.3 as float = 0.300000011920928955078125 
0.3 as double = 0.299999999999999988897769753748434595763683319091796875 
0.7 as float = 0.699999988079071044921875 
0.7 as double = 0.6999999999999999555910790149937383830547332763671875 

Alors qui explique pourquoi cela arrive ... mais il n'a pas expliquez comment contourner le problème pour ce que votre code essaie de faire, bien sûr. Si vous pouvez donner plus de contexte au plus gros problème, nous pourrons peut-être vous aider davantage.

+0

En fait, cette question a été soulevée sur un test que je pratiquais. –

3

Les ordinateurs ne peuvent pas stocker des nombres à virgule flottante exactement. Tout comme 1/7 ne peut pas être représenté dans un nombre fini de chiffres décimaux, beaucoup de nombres ne peuvent pas être représentés exactement en binaire. 3/10 est un tel nombre. Lorsque vous écrivez 0.3 votre programme stocke effectivement 0.30000001192092895508 car c'est le mieux qu'il peut faire avec les 32 bits disponibles dans une variable float.

Et il se trouve que cette valeur également diffère de la valeur double de 0.3 depuis l'ordinateur peut stocker plus de chiffres dans un 64 bits double. Lorsque vous écrivez if (x == 0.3), votre valeur est réellement promue à double puisque les constantes à virgule flottante sont double s, sauf indication contraire explicite. C'est équivalent à écrire if ((double) x == 0.3).

jkugelman$ cat float.c 
#include <stdio.h> 

int main() { 
    printf("%.20f\n", (float) 0.3); // Can also be written "0.3f". 
    printf("%.20f\n", (double) 0.3); // Cast is redundant, actually. 
    return 0; 
} 

jkugelman$ gcc -Wall -o float float.c 

jkugelman$ ./float 
0.30000001192092895508 
0.29999999999999998890 

Remarquez que la valeur 0.2999... a plus de 9 JOUE DEDANS que la 0.3000... un. La valeur de double précision est plus proche de 0.3 grâce aux bits supplémentaires.

+0

Cela n'entraînerait pas l'échec de '==' si c'était la même valeur inexacte des deux côtés. Mais le 'float' le plus proche de 0.3 n'est pas le même que le' double' le plus proche de 0.3. – dan04

+0

Donc, ma valeur est réellement stockée comme un float 32bit. Mais quand je l'utilise quelque part, comme dans if-else, il est implicitement promu en double et toutes les opérations sont effectuées sur la double valeur. Est-ce correct? –

+1

@naruto: C'est certainement le cas lorsque vous le comparez avec un flottant 64 bits. Je ne suis pas sûr qu'il y aurait une promotion en interne si vous le compariez avec un autre «float», mais comme les deux valeurs seraient promues dans ce cas, je doute que cela fasse une différence. –