Bagaimana cara kerja program ini?

88
#include <stdio.h>

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

Ini menampilkan 0!! Bagaimana mungkin? Apa alasannya?


Saya sengaja meletakkan %ddalam printfpernyataan untuk mempelajari perilaku printf.

Lazer
sumber

Jawaban:

239

Itu karena %dmengharapkan inttetapi Anda telah menyediakan pelampung.

Gunakan %e/ %f/ %guntuk mencetak float.


Tentang mengapa 0 dicetak: Angka floating point diubah menjadi doublesebelum mengirim ke printf. Angka 1234,5 dalam representasi ganda di little endian adalah

00 00 00 00  00 4A 93 40

A %dmengkonsumsi integer 32-bit, jadi nol dicetak. (Sebagai ujian, Anda printf("%d, %d\n", 1234.5f);bisa mendapatkan hasil 0, 1083394560.)


Adapun mengapa floatdiubah menjadi double, seperti prototipe printf int printf(const char*, ...), dari 6.5.2.2/7,

Notasi elipsis dalam deklarator prototipe fungsi menyebabkan konversi tipe argumen berhenti setelah parameter yang terakhir dideklarasikan. Promosi argumen default dilakukan pada argumen trailing.

dan dari 6.5.2.2/6,

Jika ekspresi yang menunjukkan fungsi yang dipanggil memiliki tipe yang tidak menyertakan prototipe, promosi integer dilakukan pada setiap argumen, dan argumen yang memiliki tipe floatdipromosikan ke double. Ini disebut promosi argumen default .

(Terima kasih Alok telah menemukan ini.)

kennytm
sumber
4
+1 Jawaban terbaik. Ini menjawab "mengapa secara teknis benar standar" dan "kemungkinan penerapan Anda" mengapa.
Chris Lutz
12
Saya yakin Anda adalah satu-satunya dari 12 orang yang benar-benar memberikan jawaban yang dia cari.
Gabe
11
Karena printfmerupakan fungsi variadik, dan standar mengatakan bahwa untuk fungsi variadic, a floatdiubah menjadi doublesebelum meneruskan.
Alok Singhal
8
Dari standar C: "Notasi elipsis dalam deklarator prototipe fungsi menyebabkan konversi jenis argumen berhenti setelah parameter yang terakhir dideklarasikan. Promosi argumen default dilakukan pada argumen tambahan." dan "... dan argumen yang memiliki tipe float dipromosikan menjadi ganda. Ini disebut promosi argumen default ."
Alok Singhal
2
Menggunakan penentu format yang salah dalam printf()memanggil Undefined Behavior .
Prasoon Saurav
45

Secara teknis tidak ada yang printf , masing-masing alat perpustakaan sendiri, dan karena itu, metode Anda mencoba untuk studi printf's perilaku dengan melakukan apa yang Anda lakukan tidak akan banyak berguna. Anda dapat mencoba mempelajari perilaku printfdi sistem Anda, dan jika demikian, Anda harus membaca dokumentasi, dan melihat kode sumber untuk mengetahui printfapakah itu tersedia untuk perpustakaan Anda.

Misalnya, di Macbook saya, saya mendapatkan output 1606416304dengan program Anda.

Karena itu, saat Anda meneruskan a floatke fungsi variadic, maka floatakan diteruskan sebagai a double. Jadi, program Anda sama dengan mendeklarasikan asebagai double.

Untuk memeriksa byte dari a double, Anda dapat melihat jawaban untuk pertanyaan terbaru ini di SO.

Ayo lakukan itu:

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

Ketika saya menjalankan program di atas, saya mendapatkan:

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

Jadi, empat byte pertama doubleternyata 0, yang mungkin menjadi alasan Anda mendapatkan 0output printfpanggilan Anda .

Untuk hasil yang lebih menarik, kita bisa sedikit mengubah programnya:

#include <stdio.h>

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

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

Ketika saya menjalankan program di atas di Macbook saya, saya mendapatkan:

42 1606416384

Dengan program yang sama di mesin Linux, saya mendapatkan:

0 1083394560
Alok Singhal
sumber
Mengapa Anda memprogram cetak mundur? Apakah saya melewatkan sesuatu? Jika b adalah a int = 42, dan '% d' adalah format integer, mengapa ini bukan nilai cetak kedua, karena ini adalah variabel kedua dalam argumen printf? Apakah ini salah ketik?
Katastic Voyage
Mungkin karena intargumen diteruskan dalam register yang berbeda dari doubleargumen. printfdengan %dmengambil intargumen itu 42 dan yang kedua %dmungkin mencetak sampah karena tidak ada intargumen kedua .
Alok Singhal
20

The %dspecifier mengatakan printfuntuk mengharapkan integer. Jadi empat (atau dua, tergantung pada platform) pertama byte float diinterpretasikan sebagai integer. Jika nilainya nol, angka nol akan dicetak

Representasi biner dari 1234.5 adalah seperti ini

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

Dengan kompiler C yang mewakili floatsebenarnya sebagai nilai ganda IEEE754, byte akan (jika saya tidak membuat kesalahan)

01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000

Pada sistem Intel (x86) dengan sedikit endianess (yaitu byte paling signifikan datang lebih dulu), urutan byte ini dibalik sehingga empat byte pertama adalah nol. Artinya, apa yang printfdicetak ...

Lihat artikel Wikipedia ini untuk representasi floating point menurut IEEE754.

MartinStettner
sumber
7

Itu karena representasi float dalam biner. Konversi ke integer meninggalkannya dengan 0.

Shaihi
sumber
1
Anda tampaknya satu-satunya yang mengerti apa yang dia minta. Kecuali jika saya salah mengira diri sendiri tentunya.
Mizipzor
Saya setuju bahwa jawabannya tidak akurat dan tidak lengkap, tetapi tidak salah.
Shaihi
7

Karena Anda memanggil perilaku tidak terdefinisi: Anda melanggar kontrak metode printf () dengan berbohong kepadanya tentang tipe parameternya, sehingga kompilator bebas melakukan apa pun yang diinginkannya. Itu bisa membuat keluaran program "dksjalk is a ninnyhead !!!" dan secara teknis itu masih benar.

Kilian Foth
sumber
5

Alasannya adalah itu printf()adalah fungsi yang sangat bodoh. Itu tidak memeriksa jenis sama sekali. Jika Anda mengatakan argumen pertama adalah int(dan ini yang Anda katakan %d), argumen itu mempercayai Anda dan hanya membutuhkan byte yang diperlukan untuk file int. Dalam kasus ini, dengan asumsi mesin Anda menggunakan empat-byte intdan delapan-byte double( floatdiubah menjadi bagian doubledalam printf()), empat byte pertama ahanya akan menjadi nol, dan ini akan dicetak.

Gorpik
sumber
3

Itu tidak akan mengubah float secara otomatis menjadi integer. Karena keduanya memiliki format penyimpanan yang berbeda. Jadi jika Anda ingin mengonversi, gunakan (int) typecasting.

#include <stdio.h>

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

Karena Anda juga menandainya dengan C ++, kode ini melakukan konversi seperti yang Anda harapkan:

#include <iostream.h>

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

Keluaran:

1234.5 1234
Mizipzor
sumber
1

%d adalah desimal

%f mengambang

lihat lebih banyak di sini .

Anda mendapatkan 0 karena float dan integer direpresentasikan secara berbeda.

Filip Ekberg
sumber
1

Anda hanya perlu menggunakan penentu format yang sesuai (% d,% f,% s, dll.) Dengan tipe data yang relevan (int, float, string, dll.).

Muhammad Maqsoodur Rehman
sumber
Pertanyaannya bukanlah bagaimana cara memperbaikinya tetapi mengapa itu berhasil seperti itu.
ya23
0

Anda ingin% f bukan% d

SC Madsen
sumber
0

hey itu harus mencetak sesuatu sehingga mencetak 0. Ingat di C 0 adalah segalanya!

chunkyguy
sumber
1
Bagaimana bisa demikian? Di C tidak ada yang namanya yang lainnya.
Vlad
jika x adalah sesuatu, maka! x == 0 :)
chunkyguy
if (iGot == "everything") print "everything"; jika tidak mencetak "tidak ada";
nik
0

Ini bukan bilangan bulat. Coba gunakan %f.

tangrs
sumber
Saya rasa pertanyaannya adalah, mengapa tidak mengubah float menjadi int dan menampilkan "1234"?
Björn Pollex
jangan berharap c tidak membiarkan Anda melakukan sesuatu yang tidak masuk akal. Ya, banyak bahasa akan memberikan 1234 yang Anda harapkan, dan bahkan mungkin beberapa implementasi c saya tidak berpikir perilaku ini didefinisikan. C memungkinkan Anda menggantung diri sendiri seperti orang tua yang memungkinkan Anda mencoba memecahkannya.
Tayangkan ulang
Karena C didesain agar fleksibel.
tangrs