#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", a);
return 0;
}
มันแสดง0
!! เป็นไปได้อย่างไร? เหตุผลคืออะไร?
ฉันได้ใส่จงใจ%d
ในคำสั่งเพื่อศึกษาพฤติกรรมของprintf
printf
#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", a);
return 0;
}
มันแสดง0
!! เป็นไปได้อย่างไร? เหตุผลคืออะไร?
ฉันได้ใส่จงใจ%d
ในคำสั่งเพื่อศึกษาพฤติกรรมของprintf
printf
คำตอบ:
นั่นเป็นเพราะ%d
คาดหวังint
แต่คุณได้ให้การลอยตัว
ใช้%e
/ %f
/ %g
เพื่อพิมพ์ลอย
เกี่ยวกับเหตุผลที่ 0 จะมีการพิมพ์: จำนวนจุดลอยจะถูกแปลงเป็นก่อนที่จะส่งไปยังdouble
printf
ตัวเลข 1234.5 ในการแสดงสองครั้งใน endian น้อยคือ
00 00 00 00 00 4A 93 40
A %d
ใช้จำนวนเต็ม 32 บิตดังนั้นจึงพิมพ์ศูนย์ (จากการทดสอบคุณprintf("%d, %d\n", 1234.5f);
สามารถได้รับผลลัพธ์0, 1083394560
)
เหตุใดจึงfloat
ถูกแปลงdouble
เป็นต้นแบบของ printf int printf(const char*, ...)
จาก 6.5.2.2/7
สัญกรณ์จุดไข่ปลาในตัวประกาศต้นแบบฟังก์ชันทำให้การแปลงประเภทอาร์กิวเมนต์หยุดหลังจากพารามิเตอร์ที่ประกาศครั้งสุดท้าย การส่งเสริมอาร์กิวเมนต์เริ่มต้นจะดำเนินการกับอาร์กิวเมนต์ต่อท้าย
และจาก 6.5.2.2/6
ถ้านิพจน์ที่แสดงถึงฟังก์ชันที่เรียกว่ามีชนิดที่ไม่มีต้นแบบการส่งเสริมจำนวนเต็มจะดำเนินการกับแต่ละอาร์กิวเมนต์และอาร์กิวเมนต์ที่มีชนิด
float
จะถูกเลื่อนdouble
ระดับ เหล่านี้จะถูกเรียกว่าโปรโมชั่นเริ่มต้นการโต้แย้ง
(ขอบคุณ Alok ที่ค้นหาสิ่งนี้)
printf
เป็นฟังก์ชันแบบแปรผันและมาตรฐานบอกว่าสำหรับฟังก์ชันตัวแปร a float
จะถูกแปลงเป็นdouble
ก่อนที่จะส่งผ่าน
printf()
จะเรียกพฤติกรรมที่ไม่ได้กำหนด
เทคนิคการพูดไม่มีแต่ละดำเนินห้องสมุดของตัวเองและดังนั้นวิธีการที่คุณพยายามที่จะศึกษาพฤติกรรมโดยการทำสิ่งที่คุณทำไม่ได้ไปจะมีการใช้มาก คุณอาจพยายามศึกษาลักษณะการทำงานของระบบของคุณและหากเป็นเช่นนั้นคุณควรอ่านเอกสารประกอบและดูที่ซอร์สโค้ดว่ามีให้สำหรับไลบรารีของคุณหรือไม่ printf
printf
printf
printf
ตัวอย่างเช่นบน Macbook ของฉันฉันได้รับผลลัพธ์1606416304
จากโปรแกรมของคุณ
ต้องบอกว่าเมื่อคุณส่งผ่านfloat
ไปยังฟังก์ชันตัวแปรfloat
จะถูกส่งผ่านเป็นไฟล์double
. ดังนั้นโปรแกรมของคุณจึงเทียบเท่ากับการประกาศa
เป็นไฟล์double
.
หากต้องการตรวจสอบไบต์ของ a double
คุณสามารถดูคำตอบสำหรับคำถามล่าสุดได้ที่ SO
มาทำกัน:
#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;
}
เมื่อฉันเรียกใช้โปรแกรมข้างต้นฉันจะได้รับ:
size of double: 8, int: 4
00 00 00 00 00 4a 93 40
ดังนั้นสี่ไบต์แรกdouble
จึงกลายเป็น 0 ซึ่งอาจเป็นสาเหตุที่คุณได้รับ0
เป็นผลลัพธ์ของการprintf
โทรของคุณ
เพื่อผลลัพธ์ที่น่าสนใจยิ่งขึ้นเราสามารถเปลี่ยนโปรแกรมได้เล็กน้อย:
#include <stdio.h>
int main(void)
{
double a = 1234.5f;
int b = 42;
printf("%d %d\n", a, b);
return 0;
}
เมื่อฉันเรียกใช้โปรแกรมข้างต้นบน Macbook ของฉันฉันจะได้รับ:
42 1606416384
ด้วยโปรแกรมเดียวกันบนเครื่อง Linux ฉันจะได้รับ:
0 1083394560
int
อาร์กิวเมนต์ถูกส่งผ่านในรีจิสเตอร์ที่แตกต่างจากdouble
อาร์กิวเมนต์ printf
โดย%d
ใช้int
อาร์กิวเมนต์ที่เป็น 42 และครั้งที่สอง%d
อาจพิมพ์ขยะเนื่องจากไม่มีint
อาร์กิวเมนต์ที่สอง
ตัว%d
ระบุบอกprintf
ให้คาดหวังจำนวนเต็ม ดังนั้นสี่ (หรือสองตัวแรกขึ้นอยู่กับแพลตฟอร์ม) ไบต์ของโฟลตจะถูกตีความเป็นจำนวนเต็ม ถ้าพวกมันเป็นศูนย์จะมีการพิมพ์ศูนย์
การแทนค่าไบนารีของ 1234.5 เป็นสิ่งที่ต้องการ
1.00110100101 * 2^10 (exponent is decimal ...)
ด้วยคอมไพเลอร์ C ซึ่งแทนfloat
ค่าสองเท่าของ IEEE754 ไบต์จะเป็น (ถ้าฉันไม่ผิดพลาด)
01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000
ในระบบ Intel (x86) ที่มีความสิ้นสุดเพียงเล็กน้อย (เช่นไบต์ที่มีนัยสำคัญน้อยที่สุดมาก่อน) ลำดับไบต์นี้จะกลับด้านเพื่อให้สี่ไบต์แรกเป็นศูนย์ นั่นคือสิ่งที่printf
พิมพ์ออกมา ...
ดูบทความ Wikipedia นี้สำหรับการแสดงจุดลอยตัวตาม IEEE754
เนื่องจากคุณเรียกใช้พฤติกรรมที่ไม่ได้กำหนด: คุณละเมิดสัญญาของเมธอด printf () โดยโกหกเกี่ยวกับประเภทพารามิเตอร์ดังนั้นคอมไพเลอร์จึงมีอิสระที่จะทำทุกอย่างตามที่ต้องการ มันสามารถทำให้โปรแกรมออกมา "dksjalk is a ninnyhead !!!" และในทางเทคนิคก็ยังคงถูกต้อง
เหตุผลก็printf()
คือฟังก์ชั่นโง่ ๆ มันไม่ได้ตรวจสอบประเภทเลย หากคุณบอกว่าอาร์กิวเมนต์แรกคือ an int
(และนี่คือสิ่งที่คุณกำลังพูดด้วย%d
) มันจะเชื่อคุณและใช้เพียงไบต์ที่จำเป็นสำหรับint
ไฟล์. ในกรณีนี้การพิจารณาเครื่องของคุณใช้สี่ไบต์int
และแปดไบต์double
( float
ถูกแปลงเป็นdouble
ด้านในprintf()
) สี่ไบต์แรกa
จะเป็นเพียงศูนย์และจะได้รับการพิมพ์
จะไม่แปลงลอยเป็นจำนวนเต็มโดยอัตโนมัติ เนื่องจากทั้งสองมีรูปแบบการจัดเก็บที่แตกต่างกัน ดังนั้นหากคุณต้องการแปลงให้ใช้ (int) typecasting
#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", (int)a);
return 0;
}
เนื่องจากคุณติดแท็กด้วย C ++ เช่นกันรหัสนี้จึงทำการแปลงตามที่คุณคาดหวัง:
#include <iostream.h>
int main() {
float a = 1234.5f;
std::cout << a << " " << (int)a << "\n";
return 0;
}
เอาท์พุต:
1234.5 1234
คุณเพียงแค่ต้องใช้ตัวระบุรูปแบบที่เหมาะสม (% d,% f,% s ฯลฯ ) กับประเภทข้อมูลที่เกี่ยวข้อง (int, float, string ฯลฯ )
คุณต้องการให้% f ไม่ใช่% d
มันไม่ใช่จำนวนเต็ม ลองใช้%f
.