#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 %d
dalam printf
pernyataan untuk mempelajari perilaku printf
.
Jawaban:
Itu karena %d
mengharapkan int
tetapi Anda telah menyediakan pelampung.
Gunakan %e
/ %f
/ %g
untuk mencetak float.
Tentang mengapa 0 dicetak: Angka floating point diubah menjadi double
sebelum mengirim ke printf
. Angka 1234,5 dalam representasi ganda di little endian adalah
00 00 00 00 00 4A 93 40
A %d
mengkonsumsi integer 32-bit, jadi nol dicetak. (Sebagai ujian, Anda printf("%d, %d\n", 1234.5f);
bisa mendapatkan hasil 0, 1083394560
.)
Adapun mengapa float
diubah 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
float
dipromosikan kedouble
. Ini disebut promosi argumen default .
(Terima kasih Alok telah menemukan ini.)
printf
merupakan fungsi variadik, dan standar mengatakan bahwa untuk fungsi variadic, a float
diubah menjadi double
sebelum meneruskan.
printf()
memanggil Undefined Behavior .
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 printf
di sistem Anda, dan jika demikian, Anda harus membaca dokumentasi, dan melihat kode sumber untuk mengetahui printf
apakah itu tersedia untuk perpustakaan Anda.
Misalnya, di Macbook saya, saya mendapatkan output 1606416304
dengan program Anda.
Karena itu, saat Anda meneruskan a float
ke fungsi variadic, maka float
akan diteruskan sebagai a double
. Jadi, program Anda sama dengan mendeklarasikan a
sebagai 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 double
ternyata 0, yang mungkin menjadi alasan Anda mendapatkan 0
output printf
panggilan 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
int
argumen diteruskan dalam register yang berbeda dari double
argumen. printf
dengan %d
mengambil int
argumen itu 42 dan yang kedua %d
mungkin mencetak sampah karena tidak ada int
argumen kedua .
The %d
specifier mengatakan printf
untuk 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 float
sebenarnya 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 printf
dicetak ...
Lihat artikel Wikipedia ini untuk representasi floating point menurut IEEE754.
Itu karena representasi float dalam biner. Konversi ke integer meninggalkannya dengan 0.
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.
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 int
dan delapan-byte double
( float
diubah menjadi bagian double
dalam printf()
), empat byte pertama a
hanya akan menjadi nol, dan ini akan dicetak.
%d
adalah desimal
%f
mengambang
lihat lebih banyak di sini .
Anda mendapatkan 0 karena float dan integer direpresentasikan secara berbeda.
Anda hanya perlu menggunakan penentu format yang sesuai (% d,% f,% s, dll.) Dengan tipe data yang relevan (int, float, string, dll.).
Ini bukan bilangan bulat. Coba gunakan %f
.