88 votes

Comment ce programme fonctionne-t-il ?

#include <stdio.h>

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 en el printf pour étudier le comportement de printf .

238voto

KennyTM Points 232647

C'est parce que %d attend un int mais vous avez fourni un flotteur.

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


Sur la raison pour laquelle 0 est imprimé : Le nombre à virgule flottante est converti en double avant de l'envoyer à printf . Le nombre 1234.5 en représentation double en little endian est

00 00 00 00  00 4A 93 40

A %d consomme un entier de 32 bits, donc un zéro est imprimé. (En guise de test, vous pourriez printf("%d, %d\n", 1234.5f); Vous pourriez obtenir une sortie 0, 1083394560 .)


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

La notation par ellipse dans un déclarateur de prototype de fonction fait que la conversion des types d'arguments s'arrête après le dernier paramètre déclaré. Les promotions d'arguments par défaut sont effectuées sur les arguments de fin.

et de 6.5.2.2/6,

Si l'expression qui désigne 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 le type float sont promus à double . On les appelle les promotions des arguments par défaut .

(Merci à Alok d'avoir découvert cela).

45voto

Alok Singhal Points 33073

Techniquement parlant, il n'y a pas le site printf chaque bibliothèque implémente la sienne, et donc, votre méthode d'essayer d'étudier printf En faisant ce que vous faites, vous ne serez pas d'une grande utilité. Vous pourriez essayer d'étudier le comportement de printf sur votre système, et si c'est le cas, vous devriez lire la documentation, et regarder le code source pour printf s'il est disponible pour votre bibliothèque.

Par exemple, sur mon Macbook, j'obtiens le résultat suivant 1606416304 avec votre programme.

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

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

Faisons ça :

#include <stdio.h>

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 j'exécute le programme ci-dessus, j'obtiens :

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

Ainsi, les quatre premiers octets du double s'est avéré être 0, ce qui peut être la raison pour laquelle vous avez eu 0 comme la sortie de votre printf appeler.

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

#include <stdio.h>

int main(void)
{
    double a = 1234.5f;
    int b = 42;

    printf("%d %d\n", a, b);
    return 0;
}

Lorsque j'exécute le programme ci-dessus sur mon Macbook, j'obtiens :

42 1606416384

Avec le même programme sur une machine Linux, j'obtiens :

0 1083394560

20voto

MartinStettner Points 14514

El %d Le spécificateur indique printf pour attendre un nombre entier. Ainsi, les quatre (ou deux, selon la plate-forme) premiers octets du flottant sont interprétés comme un entier. S'il s'avère qu'ils sont nuls, un zéro est imprimé.

La représentation binaire de 1234.5 est quelque chose comme

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

Avec un compilateur C qui représente float En fait, en tant que valeurs doubles IEEE754, les octets seraient (si je ne me trompe pas)

01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000

Sur un système Intel (x86) avec peu d'endiannes (c'est-à-dire que l'octet le moins significatif arrive en premier), cette séquence d'octets est inversée de sorte que les quatre premiers octets sont nuls. En d'autres termes, ce que printf imprime ...

Ver Cet article de Wikipedia pour la représentation en virgule flottante selon la norme IEEE754.

7voto

Kilian Foth Points 8619

Parce que vous avez invoqué un comportement non défini : vous avez violé le contrat de la méthode printf() en lui mentant sur les types de ses paramètres, et le compilateur est donc libre de faire ce qu'il veut. Il pourrait faire en sorte que le programme affiche "dksjalk est une tête de linotte ! !!" et techniquement, il aurait toujours raison.

7voto

Shaihi Points 2617

C'est à cause de la représentation d'un flottant en binaire. La conversion en un nombre entier lui laisse 0.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X