經典 C 語言面試題:將 float 變數的位址轉為 int* 解引用,或透過 union { float f; int i; } 存取,發現 97.0f 對應的整數位元模式為 0x42C20000,但若直接 printf("%d", f) 輸出卻是 0。本文從 IEEE 754 浮點數表示、呼叫慣例、記憶體佈局三個層次徹底解釋。
float f = 97.0f;
printf("%d\n", f); // 輸出 0 (或亂碼,未定義行為)
printf("%f\n", f); // 輸出 97.000000
printf("%d\n", *(int*)&f); // 輸出 1120403456 (0x42C20000)
x86-64 System V AMD64 ABI / Windows x64 呼叫慣例規定:
RDI, RSI, RDX, RCX, R8, R9 (System V) / RCX, RDX, R8, R9 (Windows)。XMM0~XMM7 (SSE 暫存器)。printf("%d", f) 時,f 作為 **浮點數** 被放入 **XMM0**,但 %d 告訴 printf 「去整數暫存器 (RDI) 取值」。printf 讀取 RDI 內容(可能是殘留垃圾值或 0),導致輸出 0。
97.0f 在記憶體中佔 4 字節,位元模式:
符號位(1) | 指數位(8) | 尾數位(23)
0 | 10000101 | 10000100000000000000000
= 0x42C20000
若以 int 讀取這 4 字節,得到 1120403456,**這是位元模式的整數解讀,而非數值轉換**。
printf("%f", f) 正確?%f 對應 double,float 會**預設提升為 double** 傳遞。ABI 規定浮點數參數無論 float 或 double 皆放入 XMM 暫存器(低 64 位)。printf 從 XMM0 讀取 64 位雙精度值,正確解讀為 97.0。
| 目的 | 正確寫法 | 說明 |
|---|---|---|
| 輸出 float 數值 | printf("%f", f) 或 printf("%.2f", f) | %f 接受 double,float 自動提升 |
| 輸出 float 位元模式 (十六進位) | printf("0x%08X", *(unsigned int*)&f) | 透過 unsigned int* 讀取 4 字節原始位元 |
| 輸出 float 位元模式 (二進位) | 迴圈位移列印各位 | 教學用,生產罕用 |
| float 轉 int 數值 | int i = (int)f; 或 lrintf(f) | 截斷/四捨五入轉換,非位元複製 |