Come funziona questo programma?


88
#include <stdio.h>

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

Visualizza un 0!! Come è possibile? Qual è il ragionamento?


Ho deliberatamente inserito un %dnella printfdichiarazione per studiare il comportamento di printf.

Risposte:


239

Questo perché si %daspetta un intma tu hai fornito un float.

Usa %e/ %f/ %gper stampare il float.


Sul motivo per cui viene stampato 0: il numero in virgola mobile viene convertito in doubleprima dell'invio a printf. Il numero 1234.5 in doppia rappresentazione in little endian è

00 00 00 00  00 4A 93 40

A %dconsuma un numero intero a 32 bit, quindi viene stampato uno zero. (Come prova, potresti printf("%d, %d\n", 1234.5f);ottenere in uscita 0, 1083394560.)


Per quanto riguarda il motivo per cui floatviene convertito in double, come è il prototipo di printf int printf(const char*, ...), da 6.5.2.2/7,

La notazione con i puntini di sospensione 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 predefinite 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 di interi vengono eseguite su ogni argomento e gli argomenti che hanno tipo floatvengono promossi a double. Queste sono chiamate promozioni degli argomenti predefinite .

(Grazie Alok per averlo scoperto.)


4
+1 Migliore risposta. Risponde sia al perché "standard tecnicamente corretto" che alla "tua probabile implementazione".
Chris Lutz

12
Credo che tu sia l'unica delle 12 persone che ha effettivamente fornito la risposta che stava cercando.
Gabe

11
Perché printfè una funzione variadica e lo standard dice che per le funzioni variadiche, a floatviene convertito in doubleprima di passare.
Alok Singhal

8
Dallo standard C: "La notazione con i puntini di sospensione in un dichiaratore prototipo di funzione causa l'interruzione della conversione del tipo di argomento dopo l'ultimo parametro dichiarato. Le promozioni di argomento predefinite vengono eseguite sugli argomenti finali." e "... e gli argomenti che hanno tipo float vengono promossi a double. Questi sono chiamati promozioni degli argomenti predefiniti ."
Alok Singhal

2
L'utilizzo di un identificatore di formato errato in printf()richiama un comportamento indefinito .
Prasoon Saurav

45

Tecnicamente parlando non v'è la printf , ogni implementa una propria biblioteca, e, quindi, il metodo di cercare di studio printf's comportamento facendo quello che state facendo non sta per essere di grande utilità. Potresti provare a studiare il comportamento di printfsul tuo sistema e, in tal caso, dovresti leggere la documentazione e guardare il codice sorgente per verificare printfse è disponibile per la tua libreria.

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

Detto questo, quando si passa a floata una funzione variadica, floatviene passato come a double. Quindi, il tuo programma equivale ad aver dichiarato acome file double.

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

Facciamolo:

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

Quando eseguo il programma sopra, ottengo:

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

Quindi, i primi quattro byte di doublerisultano essere 0, che potrebbe essere il motivo per cui hai ricevuto 0come output della tua printfchiamata.

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

#include <stdio.h>

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

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

Quando eseguo il programma sopra sul mio Macbook, ottengo:

42 1606416384

Con lo stesso programma su una macchina Linux, ottengo:

0 1083394560

Perché hai programmato la stampa al contrario? Mi sto perdendo qualcosa? Se b è un int = 42 e '% d' è il formato intero, perché non è il secondo valore stampato, dato che è la seconda variabile negli argomenti printf? È un errore di battitura?
Viaggio Katastic

Probabilmente perché gli intargomenti vengono passati in registri diversi dagli doubleargomenti. printfwith %daccetta l' intargomento che è 42 e il secondo %dprobabilmente stampa spazzatura poiché non c'era un secondo intargomento.
Alok Singhal,

20

Lo %dspecificatore dice printfdi aspettarsi un numero intero. Quindi i primi quattro (o due, a seconda della piattaforma) byte del float vengono interpretati come un numero intero. Se sono 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 in floatrealtà come valori doppi IEEE754, i byte sarebbero (se non avessi commesso errori)

01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000

Su un sistema Intel (x86) con poca endianess (cioè il byte meno significativo che arriva per primo), questa sequenza di byte viene invertita in modo che i primi quattro byte siano zero. Cioè, ciò che viene printfstampato ...

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


7

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


1
Sembra che tu sia l'unico che ha capito quello che stava chiedendo. A meno che non mi sbagli, ovviamente.
Mizipzor

Sono d'accordo che la risposta sia imprecisa e incompleta, ma non sbagliata.
Shaihi

7

Perché hai invocato un comportamento indefinito: hai violato il contratto del metodo printf () mentendogli sui suoi tipi di parametro, quindi il compilatore è libero di fare tutto ciò che vuole. Potrebbe far uscire il programma "dksjalk is a ninnyhead !!!" e tecnicamente sarebbe ancora giusto.


5

Il motivo è che printf()è una funzione piuttosto stupida. Non controlla affatto i tipi. Se dici che il primo argomento è un int(e questo è ciò con cui stai dicendo %d), ti crede e prende solo i byte necessari per un file int. In questo caso, supponendo che la tua macchina usi quattro byte inte otto byte double(il floatviene convertito in un doubleinterno printf()), i primi quattro byte di asaranno solo zeri, e questo verrà stampato.


3

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

#include <stdio.h>

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

2

Dato che lo hai etichettato anche con C ++, questo codice esegue la conversione come probabilmente ti aspetti:

#include <iostream.h>

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

Produzione:

1234.5 1234

1

%d è decimale

%f è galleggiante

vedi più di questi qui .

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


1

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


La domanda non è come risolverlo, ma perché funziona nel modo in cui funziona.
ya23


0

hey doveva stampare qualcosa, quindi ha stampato uno 0. Ricorda in C 0 è tutto il resto!


1
Come è possibile? In C non c'è niente come tutto il resto.
Vlad

se x è qualcosa, allora! x == 0 :)
chunkyguy

if (iGot == "tutto") stampa "tutto"; altrimenti stampa "niente";
nik

0

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


Immagino che la domanda sia: perché non converte semplicemente il float in int e mostra "1234"?
Björn Pollex

non aspettarti che c non ti lasci fare qualcosa che non ha senso logico. Sì, molte lingue darebbero il 1234 che potresti aspettarti, e forse anche alcune implementazioni di c non credo che questo comportamento sia definito. C ti permette di impiccarti, è come il genitore che ti lascia provare il crack per il gusto di farlo.
replica il

Perché C è progettato per essere flessibile.
tangrs
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.