Comment fonctionne ce programme?

#include  int main() { float a = 1234.5f; printf("%d\n", a); return 0; } 

Il affiche un 0 !! Comment est-ce possible? Quel est le raisonnement?


J’ai délibérément mis un %d dans l’instruction printf pour étudier le comportement de printf .

C’est parce que %d attend un int mais vous avez fourni un float.

Utilisez %e / %f / %g pour imprimer le flottant.


On pourquoi 0 est imprimé: le nombre à virgule flottante est converti en double avant d’être envoyé à printf . Le nombre 1234.5 en double représentation en little endian est

 00 00 00 00 00 4A 93 40 

Un %d consum un entier de 32 bits, donc un zéro est imprimé. (A titre de test, vous pouvez printf("%d, %d\n", 1234.5f); vous pouvez obtenir la sortie 0, 1083394560 )


Quant à savoir pourquoi le float est converti en double , comme le prototype de printf est int printf(const char*, ...) , à partir du 6.5.2.2/7,

La notation des points de suspension dans un déclarant de prototype de fonction entraîne l’arrêt de la conversion du type d’argument après le dernier paramètre déclaré. Les promotions d’argument par défaut sont effectuées sur les arguments de fin.

et du 6.5.2.2/6,

Si l’expression qui dénote la fonction appelée a un type qui n’inclut pas de prototype, les promotions entières sont effectuées sur chaque argument et les arguments qui ont un type float sont promus à double . Celles-ci s’appellent les promotions d’argument par défaut .

(Merci Alok de l’avoir découvert.)

Techniquement parlant, il n’y a pas de printf , chaque bibliothèque implémente ses propres méthodes et, par conséquent, votre méthode pour essayer d’étudier le comportement de printf en faisant ce que vous faites ne sera pas très utile. Vous pourriez essayer d’étudier le comportement de printf sur votre système et, si tel est le cas, vous devriez lire la documentation et consulter le code source de printf s’il est disponible pour votre bibliothèque.

Par exemple, sur mon Macbook, je reçois la sortie 1606416304 avec votre programme.

Cela dit, lorsque vous passez un float à une fonction variadique, le float est transmis en double . Ainsi, votre programme équivaut à avoir déclaré un double .

Pour examiner les octets d’un double , vous pouvez voir cette réponse à une question récente sur SO.

Faisons cela:

 #include  int main(void) { double a = 1234.5f; unsigned char *p = (unsigned char *)&a; size_t i; printf("size of double: %zu, int: %zu\n", sizeof(double), sizeof(int)); for (i=0; i < sizeof a; ++i) printf("%02x ", p[i]); putchar('\n'); return 0; } 

Lorsque je lance le programme ci-dessus, je reçois:

 size of double: 8, int: 4 00 00 00 00 00 4a 93 40 

Ainsi, les quatre premiers octets du double se sont avérés être 0, ce qui peut être la raison pour laquelle vous avez obtenu 0 comme sortie de votre appel printf .

Pour des résultats plus intéressants, nous pouvons changer un peu le programme:

 #include  int main(void) { double a = 1234.5f; int b = 42; printf("%d %d\n", a, b); return 0; } 

Lorsque je lance le programme ci-dessus sur mon Macbook, je reçois:

 42 1606416384 

Avec le même programme sur une machine Linux, je reçois:

 0 1083394560 

Le spécificateur %d dit à printf d’attendre un entier. Ainsi, les quatre premiers octets (ou deux, selon la plate-forme) du nombre entier sont interprétés comme un entier. S’ils sont nuls, un zéro est imprimé

La représentation binary de 1234.5 est quelque chose comme

 1.00110100101 * 2^10 (exponent is decimal ...) 

Avec un compilateur C qui représente réellement float comme double valeur IEEE754, les octets seraient (si je ne me trompais pas)

 01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000 

Sur un système Intel (x86) avec peu d’endianess (c’est-à-dire l’octet le moins significatif venant en premier), cette séquence d’octets est inversée pour que les quatre premiers octets soient nuls. C’est-à-dire ce que printf imprime …

Voir Cet article Wikipedia pour la représentation en virgule flottante selon IEEE754.

C’est à cause de la représentation d’un flottant en binary. La conversion en entier le laisse avec 0.

Parce que vous avez invoqué un comportement non défini: vous avez violé le contrat de la méthode printf () en lui mentant ses types de parameters, de sorte que le compilateur est libre de faire ce qu’il veut. Cela pourrait rendre la sortie du programme “dksjalk est un ninnyhead !!!” et techniquement, ce serait toujours bien.

La raison en est que printf() est une fonction assez stupide. Il ne vérifie pas du tout les types. Si vous dites que le premier argument est un int (et c’est ce que vous dites avec %d ), il vous croit et il ne prend que les octets nécessaires pour un int . Dans ce cas, si votre machine utilise quatre octets int et double octet double (le float est converti en double intérieur de printf() ), les quatre premiers octets de a seront juste des zéros, et ceci sera imprimé.

Il ne sera pas converti automatiquement en nombre entier. Parce que les deux ont un format de stockage différent. Donc, si vous voulez convertir, utilisez (int) typecasting.

 #include  int main() { float a = 1234.5f; printf("%d\n", (int)a); return 0; } 

Comme vous l’avez également tagué avec C ++, ce code effectue la conversion comme vous vous en doutez probablement:

 #include  int main() { float a = 1234.5f; std::cout << a << " " << (int)a << "\n"; return 0; } 

Sortie:

 1234.5 1234 

%d est décimal

%f est float

voir plus de ces ici .

Vous obtenez 0 car les flottants et les entiers sont représentés différemment.

Il vous suffit d’utiliser le spécificateur de format approprié (% d,% f,% s, etc.) avec le type de données approprié (int, float, ssortingng, etc.).

Vous voulez% f pas% d

Hé, il fallait imprimer quelque chose, donc il imprimait un 0. Rappelez-vous que dans C 0, tout est autre chose!

Ce n’est pas un entier. Essayez d’utiliser %f .