較舊的 Delphi 程式碼有時會包含一個小型 _ftol 輔助函式,用於把浮點值轉換為整數。在 32 位專案中,這類輔助函式常用內聯彙編實現,尤其常見於為舊編譯器調優過的圖形和 PDF 程式碼。
|
1 2 3 4 5 6 7 8 |
function _ftol( f: double) : Integer; cdecl; begin asm lea eax, f fstp qword ptr [eax] end; result := Trunc(f); end; |
同一個單元編譯為 64 位目標時,這種寫法會失敗。Delphi 64 位編譯器會報告 E1025 Unsupported language feature: 'ASM',因為 DCC64 不支援在普通 Pascal 例程內部混合 Pascal 和內聯彙編。
為什麼 64 位編譯器會拒絕
DCC64 可以彙編 64 位彙編例程,但例程必須作為完整的 assembler routine 編寫,並遵守 64 位呼叫約定。不能把 32 位內聯彙編複製到 Pascal 函式體裡,指望編譯器自動轉換暫存器使用、棧佈局或 x87 行為。
完整彙編版本是可能的,下面的程式碼展示了當值已經位於 FPU 棧上時,可能需要怎樣處理控制字。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function _ftol: Integer; cdecl; // Assumes double value is in FPU stack on entry // Make a truncation to integer and put it into function result var TmpVal: Int64; SaveCW, ScratchCW: word; asm .NOFRAME fnstcw word ptr [SaveCW] fnstcw word ptr [ScratchCW] or word ptr [ScratchCW], 0F00h ;// trunc toward zero, full precision fldcw word ptr [ScratchCW] fistp qword ptr [TmpVal] fldcw word ptr [SaveCW] mov rax, TmpVal end; |
只需要截斷時優先使用 Pascal
對大多數應用程式碼來說,彙編例程並不必要。如果真實需求只是把 Double 向零截斷並返回整數值,清晰且可移植的版本就是:
|
1 2 3 4 |
function _ftol(f: double): integer; cdecl; begin Result := Trunc(f); end; |
Trunc 直接表達意圖,可同時編譯到 32 位和 64 位目標,並避免維護架構相關彙編的成本。後續維護者也更容易理解範圍檢查和溢位行為。
範圍和行為注意事項
用 Trunc 替換匯編仍應被視為需要測試的行為變更。請確認負數、接近整數邊界的小數,以及超出 Integer 範圍的值應該如何處理。舊彙編可能依賴 FPU 控制字狀態,而 Pascal 版本遵循目標編譯器的 RTL 實現。
如果該輔助函式位於 PDF 或圖形元件中,請測試旋轉文字、縮放座標和變換後的繪製命令。這些區域比簡單示例更容易暴露浮點到整數轉換的細微差異。
推薦遷移路徑
- 如果只需要數值轉換,優先使用清晰的 Pascal 實現。
- 只有在二進位制相容性需要特定符號和呼叫約定時,才保留彙編匯出。
- 不要在沒有審查呼叫約定和資料寬度的情況下複製 32 位暫存器程式碼。
- 測試負數、大數以及超出目標整數範圍的值。