Pourquoi printf () ne sort pas cet entier sous forme de nombre flottant?

J’ai ce code:

#include  int main() { int i = 12345; printf("%f", i); return 0; } 

printf () affichera 0.000000, printf () ne devrait-il pas interpréter les bits contenus dans i comme un nombre flottant?

Techniquement, c’est un comportement indéfini .

Pour ce faire, vous devez utiliser un syndicat et l’imprimer avec %e au lieu de %f , car il s’agit d’un petit nombre:

 #include  union int2float { int a; float b; }; int main() { union int2float tmp; tmp.a = 12345; printf("%e\n", tmp.b); return 0; } 

Cela donne 1.729903e-41 .

avec %.100f vous obtiendrez en sortie 0.0000000000000000000000000000000000000000172990295420898667405534417057140146406548336724655872023410

Il s’agit d’un comportement techniquement indéfini. Tout peut donc arriver, par exemple ce que @Art décrit dans sa réponse.

Cette réponse est donc une explication de ce qui se passerait si vous tentiez d’imprimer 12345 en tant que float, en supposant que printf voit effectivement la valeur 32 bits correcte.

Analysons la représentation binary du nombre que vous essayez de représenter.

En utilisant http://www.h-schmidt.net/FloatConverter/IEEE754.html, nous pouvons voir que le nombre décimal 12345 a la représentation 32 bits suivante:

 decimal 12345 hexadecimal 0x00003039 

Converti bit à bit en une valeur 32 bits à virgule flottante IEEE-754, cela représente:

 float 1.7299E-41 

essayons de l’imprimer:

 #include  int main() { printf("%f\n", 1.7299E-41); return 0; } 

Cela imprime:

 0.000000 

Maintenant, lisons la page de manuel de printf , telle que trouvée ici: http://linux.die.net/man/3/printf

f, f

Le double argument est arrondi et converti en notation décimale dans le style [-] ddd.ddd, où le nombre de chiffres après le caractère décimal est égal à la spécification de précision. Si la précision est manquante, la valeur est 6; Si la précision est explicitement égale à zéro, aucun caractère décimal n’apparaît. Si un point décimal apparaît, il y a au moins un chiffre devant celui-ci.

La valeur 1.7299E-41 ne pouvant pas être représentée à l’aide de cet indicateur avec la précision par défaut, le résultat obtenu correspond bien à la valeur correcte.

La raison la plus probable pour laquelle printf n’interprète pas les bits de i tant que nombre à virgule flottante est que printf ne le voit pas du tout. Je suppose que vous utilisez x86_64 ou une plate-forme similaire où les arguments sont transmis aux fonctions des registres. Normalement, printf interprétera votre int comme le spécificateur de format que vous lui avez donné. Mais les arguments en virgule flottante des fonctions sont gérés différemment sur x86_64 et sont placés dans des registres différents. Donc, la raison pour laquelle vous obtenez 0 en sortie est que printf extrait le premier argument en virgule flottante après la chaîne de format au lieu du premier registre à usage général et l’imprime. Puisque vous n’avez utilisé aucun registre à virgule flottante avant votre appel à printf ils sont probablement mis à zéro dès le début du programme.

Voici une expérience que vous pouvez essayer:

 #include  int main(int argc, char **argv) { printf("%f\n", 17.42); printf("%f\n", 0x87654321); return 0; } 

Je reçois cette sortie:

 $ ./foo 17.420000 17.420000 

Cela dit, la seule réponse correcte est: c’est un comportement indéfini, ne le faites pas. Mais il est intéressant de voir ce qui se passe sous le capot.

Si vous voulez plonger dans cette question et que vous utilisez ce document sous Linux / MacOS / * BSD , la section 3.2.3 décrit comment les arguments sont transmis aux fonctions (y compris les fonctions varargs telles que printf). Windows fait les choses légèrement différemment, mais dans ce cas, le résultat sous Windows serait exactement le même. printf sous Linux / MacOS / * BSD s’attend à ce que l’argument soit imprimé sous %xmm0 ( %xmm1 sous Windows), tandis que votre appel le passe sous %rsi ( %rdx sous Windows).

Bien sûr, si je me trompe à propos de x86_64, ignorez tout ce que j’ai dit et examinez la réponse de @SirDarius, car cela s’applique à de nombreuses architectures (en particulier les plus anciennes). Si vous êtes sur un alpha, sparc, powerpc, arm, etc. vous êtes seul. Appelez une fois printf avec un int, une fois avec un float, désassemblez le code compilé et voyez comment les arguments sont passés.

Si vous voulez imprimer un int comme float, lancez-le:

 printf("%f", (float)i); 

La seule manière dont printf() connaisse le type de données des arguments après la chaîne de formatage (et leur nombre) est en lisant la chaîne de formatage. La représentation d’un entier en mémoire est différente d’un nombre à virgule flottante. Vous transmettez des données printf () au format d’un nombre entier, mais elles prennent le format d’un nombre à virgule flottante. C’est pourquoi vous obtenez des ordures dans la console. Vous pouvez corriger cela par la transtypage.

 printf("%f", (float) i); 

lorsque vous utilisez le spécificateur de format %f , vous devez utiliser la variable float et imprimer la valeur. mais vous avez déclaré i comme entier et essayez d’imprimer avec %f il affichera une valeur. Comme il s’agit d’un entier, vous devez utiliser le spécificateur %d . Dans printf %f recherchera la variable float. mais il n’y aura pas de variable float, donc cela affichera une valeur.

Vous pouvez saisir la valeur entière lors de l’impression. printf("%f",(float)i);

Pour que i sorte de type float vous devez faire un transtypage.

Vous pouvez utiliser

 printf("%f", (float)i); 

Exemple.

 float myFloat; int myInt; myFloat = (float)myInt ; // Type casting 

Essaye ça

 int i = 12345; printf("%.2f", i/1.0f);