Come funziona questo programma?

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

Visualizza uno 0 !! Come è ansible? Qual è il ragionamento?


Ho deliberatamente messo un %d printf per studiare il comportamento di printf .

Questo perché %d aspetta un int ma tu hai fornito un float.

Usa %e / %f / %g per stampare il float.


Perché viene stampato 0: il numero in virgola mobile viene convertito in double prima di inviarlo a printf . Il numero 1234.5 in doppia rappresentazione in little endian è

 00 00 00 00 00 4A 93 40 

A %d un numero intero a 32 bit, quindi viene stampato uno zero. (Come test, è ansible printf("%d, %d\n", 1234.5f); È ansible ottenere l’output 0, 1083394560 )


Per quanto riguarda il motivo per cui il float viene convertito in double , dal momento che il prototipo di printf è int printf(const char*, ...) , dal 6.5.2.2/7,

La notazione di ellissi in un dichiaratore di prototipo di funzione causa l’interruzione della conversione del tipo di argomento dopo l’ultimo parametro dichiarato. Le promozioni degli argomenti predefiniti vengono eseguite sugli argomenti finali.

e dal 6.5.2.2/6,

Se l’espressione che denota la funzione chiamata ha un tipo che non include un prototipo, le promozioni intere vengono eseguite su ogni argomento e gli argomenti che hanno il tipo float vengono promossi a double . Queste sono chiamate promozioni di argomenti predefinite .

(Grazie Alok per averlo scoperto.)

Tecnicamente parlando non c’è la printf , ogni libreria implementa la sua, e quindi, il tuo metodo di provare a studiare il comportamento di printf facendo quello che stai facendo non sarà di grande utilità. Potresti provare a studiare il comportamento di printf sul tuo sistema, e se è così, dovresti leggere la documentazione e guardare il codice sorgente di printf se è disponibile per la tua libreria.

Ad esempio, sul mio Macbook, ottengo l’output 1606416304 con il tuo programma.

Detto questo, quando si passa un float a una funzione variadica, il float viene passato come un double . Quindi, il tuo programma equivale ad aver dichiarato un double .

Per esaminare i byte di un double , puoi vedere questa risposta a una domanda recente qui su SO.

Facciamolo:

 #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; } 

Quando eseguo il programma di cui sopra, ottengo:

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

Quindi, i primi quattro byte del double si sono rivelati 0, il che potrebbe essere il motivo per cui hai ottenuto 0 come risultato della tua chiamata printf .

Per risultati più interessanti, possiamo modificare un po 'il programma:

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

Quando eseguo il programma sopra riportato sul mio Macbook, ottengo:

 42 1606416384 

Con lo stesso programma su una macchina Linux, ottengo:

 0 1083394560 

Lo specificatore %d dice a printf di aspettarsi un intero. Quindi i primi quattro (o due, in base alla piattaforma), i byte del float sono interpretati come un numero intero. Se capita di essere zero, viene stampato uno zero

La rappresentazione binaria di 1234.5 è qualcosa di simile

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

Con un compilatore C che rappresenta float effettivamente come IEEE754 double values, i byte sarebbero (se non ho fatto alcun errore)

 01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000 

Su un sistema Intel (x86) con poca endianess (ovvero il byte meno significativo che viene prima), questa sequenza di byte viene invertita in modo che i primi quattro byte siano zero. Cioè, che printf stampa …

Vedi questo articolo di Wikipedia per la rappresentazione in virgola mobile secondo IEEE754.

È a causa della rappresentazione di un float in binario. La conversione in un numero lo lascia con 0.

Perché hai invocato un comportamento indefinito: hai violato il contratto del metodo printf () mentendo ai suoi tipi di parametro, quindi il compilatore è libero di fare tutto ciò che desidera. Potrebbe rendere il programma in uscita “dksjalk è un ninnyhead !!!” e tecnicamente sarebbe ancora giusto.

Il motivo è che printf() è una funzione piuttosto stupida. Non controlla affatto i tipi. Se dici che il primo argomento è un int (e questo è ciò che stai dicendo con %d ), ti crede e richiede solo i byte necessari per un int . In questo caso, se la tua macchina usa quattro byte int e otto byte double (il float viene convertito in double in printf() ), i primi quattro byte di a saranno solo zero, e questo verrà stampato.

Non converte automaticamente float in intero. Perché entrambi hanno diversi formati di archiviazione. Quindi, se vuoi convertire, usa (int) typecasting.

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

Poiché lo hai codificato anche con C ++, questo codice esegue la conversione come probabilmente ti aspetti:

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

Produzione:

 1234.5 1234 

%d è decimale

%f è float

vedere più di questi qui .

Stai ottenendo 0 perché i float e gli interi sono rappresentati in modo diverso.

È sufficiente utilizzare l’identificatore di formato appropriato (% d,% f,% s, ecc.) Con il tipo di dati pertinente (int, float, stringa, ecc.).

Vuoi% f non% d

hey ha dovuto stampare qualcosa così ha stampato uno 0. Ricorda in C 0 è tutto il resto!

Non è un numero intero. Prova a usare %f .