Comment fonctionne ce programme?


88
#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 %ddans la printfdéclaration pour étudier le comportement de printf.

Réponses:


239

C'est parce que %ds'attend à un intmais vous avez fourni un flotteur.

Utilisez %e/ %f/ %gpour imprimer le flotteur.


Pourquoi 0 est imprimé: le nombre à virgule flottante est converti en doubleavant l'envoi vers printf. Le nombre 1234.5 en double représentation en petit boutiste est

00 00 00 00  00 4A 93 40

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


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

La notation des points de suspension dans un déclarateur de prototype de fonction provoque l'arrêt de la conversion de 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 à partir du 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 d'entiers sont effectuées sur chaque argument et les arguments qui ont un type floatsont promus en double. On les appelle les promotions d'argument par défaut .

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


4
+1 Meilleure réponse. Il répond à la fois au «pourquoi standard techniquement correct» et au «pourquoi de votre implémentation probable».
Chris Lutz

12
Je crois que vous êtes la seule des 12 personnes à avoir fourni la réponse qu'il recherchait.
Gabe

11
Parce que printfc'est une fonction variadique, et la norme dit que pour les fonctions variadiques, a floatest converti en doubleavant de passer.
Alok Singhal

8
De la norme C: "La notation des points de suspension dans un déclarateur de prototype de fonction provoque l'arrêt de la conversion de 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 "... et les arguments qui ont le type float sont promus au double. On les appelle les promotions d'argument par défaut ."
Alok Singhal

2
L'utilisation d'un spécificateur de format incorrect dans printf()appelle un comportement indéfini .
Prasoon Saurav

45

Techniquement parlant, il n'y a pas de the printf , chaque bibliothèque implémente la sienne, et par conséquent, votre méthode pour essayer d'étudier printfle comportement de ce que vous faites ne sera pas d'une grande utilité. Vous pourriez essayer d'étudier le comportement de printfsur votre système, et si tel est le cas, vous devriez lire la documentation et regarder le code source pour savoir printfs'il est disponible pour votre bibliothèque.

Par exemple, sur mon Macbook, j'obtiens la sortie 1606416304avec votre programme.

Cela dit, lorsque vous passez a floatà une fonction variadique, le floatest passé comme a double. Donc, votre programme équivaut à avoir déclaré en atant que fichier double.

Pour examiner les octets de a double, vous pouvez voir cette réponse à une question récente ici sur SO.

Faisons cela:

#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 de la doublese sont avérés être 0, ce qui peut être la raison pour laquelle vous avez obtenu 0la sortie de votre printfappel.

Pour 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

Pourquoi avez-vous programmé l'impression à l'envers? Est-ce que je manque quelque chose? Si b est un int = 42 et '% d' est le format entier, pourquoi n'est-ce pas la deuxième valeur imprimée, puisque c'est la deuxième variable dans les arguments printf? Est-ce une faute de frappe?
Katastic Voyage

Probablement parce que les intarguments sont passés dans des registres différents des doublearguments. printfwith %dprend l' intargument qui est 42 et le second %dimprime probablement des fichiers indésirables puisqu'il n'y avait pas de second intargument.
Alok Singhal

20

Le %dspécificateur dit printfd'attendre un entier. Ainsi, les quatre premiers octets (ou deux, selon la plate-forme) du float sont interprétés comme un entier. S'ils se trouvent être zéro, 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 en floatfait des valeurs doubles 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'extrémité (c'est-à-dire l'octet le moins significatif venant en premier), cette séquence d'octets est inversée de sorte que les quatre premiers octets sont à zéro. Autrement dit, ce qui printfs'imprime ...

Consultez cet article de Wikipedia pour la représentation en virgule flottante selon IEEE754.


7

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


1
Vous semblez être le seul à avoir compris ce qu'il demandait. A moins que je ne me trompe bien sûr.
Mizipzor

Je reconnais que la réponse est inexacte et incomplète, mais pas fausse.
Shaihi

7

Parce que vous avez invoqué un comportement indéfini: vous avez violé le contrat de la méthode printf () en lui mentant sur ses types de paramètres, de sorte que le compilateur est libre de faire ce qu'il veut. Cela pourrait faire sortir le programme "dksjalk est un ninnyhead !!!" et techniquement, ce serait toujours vrai.


5

La raison en est que printf()c'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 avec quoi vous dites %d), il vous croit et il ne prend que les octets nécessaires pour un fichier int. Dans ce cas, en supposant que votre machine utilise quatre intet huit octets double(le floatest converti en un à l' doubleintérieur printf()), les quatre premiers octets de aseront juste des zéros, et cela sera imprimé.


3

Il ne convertira pas automatiquement le flottant en entier. Parce que les deux ont un format de stockage différent. Donc, si vous souhaitez convertir, utilisez le typage (int).

#include <stdio.h>

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

2

Puisque vous l'avez également marqué avec C ++, ce code effectue la conversion comme vous vous y attendez probablement:

#include <iostream.h>

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

Production:

1234.5 1234

1

%d est décimal

%f est flottant

voir plus de ceux-ci ici .

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


1

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


La question n'est pas de savoir comment y remédier, mais pourquoi cela fonctionne comme cela.
ya23


0

hey il fallait imprimer quelque chose donc il a imprimé un 0. Rappelez-vous en C 0 c'est tout le reste!


1
Comment ça se passe? En C, tout le reste n'existe pas.
Vlad

si x est quelque chose, alors! x == 0 :)
chunkyguy

if (iGot == "tout") affiche "tout"; sinon imprimer "rien";
nik

0

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


Je suppose que la question est, pourquoi ne convertit-il pas simplement le float en int et affiche "1234"?
Björn Pollex

ne vous attendez pas à ce que c ne vous laisse pas faire quelque chose qui n'a aucun sens logique. Oui, de nombreux langages donneraient les 1234 auxquels vous pouvez vous attendre, et peut-être même certaines implémentations de c ne penseront-elles pas que ce comportement est défini. C vous permet de vous pendre, c'est comme le parent qui vous permet d'essayer de craquer pour l'enfer.
rediffusé

Parce que C est conçu pour être flexible.
tangrs
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.