Älterer Delphi-Code enthielt manchmal einen kleinen _ftol-Helfer, um einen Gleitkommawert in eine Ganzzahl umzuwandeln. In 32-Bit-Projekten war es üblich, solche Helfer mit Inline-Assembler zu schreiben, besonders in Grafik- und PDF-Code, der für ältere Compiler optimiert war.
|
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; |
Dieser Ansatz bricht, wenn dieselbe Unit für ein 64-Bit-Ziel kompiliert wird. Der Delphi-64-Bit-Compiler meldet E1025 Unsupported language feature: 'ASM', weil DCC64 gemischtes Pascal und Inline-Assembly innerhalb normaler Pascal-Routinen nicht unterstützt.
Warum der 64-Bit-Compiler es ablehnt
DCC64 kann 64-Bit-Assembly-Routinen assemblieren, aber die Routine muss als vollständige Assemblerroutine mit der 64-Bit-Aufrufkonvention geschrieben werden. Man kann 32-Bit-Inline-Assembler nicht in einen Pascal-Funktionskörper kopieren und erwarten, dass der Compiler Registerverwendung, Stacklayout oder x87-Verhalten übersetzt.
Eine vollständige Assemblerversion ist möglich, und Code wie der folgende zeigt, welche Control-Word-Behandlung nötig sein kann, wenn der Wert bereits auf dem FPU-Stack liegt.
|
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 bevorzugen, wenn nur truncation benötigt wird
Für die meisten Anwendungen ist die Assemblerroutine unnötig. Wenn die eigentliche Anforderung darin besteht, einen Double gegen null zu kürzen und einen Integerwert zurückzugeben, ist die klare und portable Version einfach:
|
1 2 3 4 |
function _ftol(f: double): integer; cdecl; begin Result := Trunc(f); end; |
Trunc drückt die Absicht direkt aus, kompiliert für 32- und 64-Bit-Ziele und vermeidet Wartungskosten architekturspezifischer Assembly. Außerdem können spätere Maintainer Range-Checks und Overflow-Verhalten leichter beurteilen.
Migrationshinweise
- Verwenden Sie die Pascal-Implementierung, wenn Sie die Aufrufstelle kontrollieren und nur numerische Konvertierung benötigen.
- Behalten Sie einen Assembler-Export nur bei, wenn Binärkompatibilität ein bestimmtes Symbol und eine bestimmte Aufrufkonvention verlangt.
- Kopieren Sie keinen 32-Bit-Registercode in eine 64-Bit-Routine, ohne Aufrufkonvention und Datenbreite zu prüfen.
- Testen Sie Grenzfälle wie negative Werte, große Werte und Werte außerhalb des Ziel-Integerbereichs.
Hinweise zu Bereich und Verhalten
Das Ersetzen von Assembler durch Trunc sollte dennoch als Verhaltensänderung mit Tests behandelt werden. Bestätigen Sie das gewünschte Verhalten für negative Zahlen, Bruchteile nahe einer Ganzzahlgrenze und Werte außerhalb des Bereichs von Integer. Der alte Assembler kann vom FPU-Control-Word-Zustand abhängig gewesen sein, während die Pascal-Version der RTL-Implementierung des Zielcompilers folgt.
Wenn der Helfer in einer PDF- oder Grafikkomponente sitzt, testen Sie Ausgaben mit rotiertem Text, skalierten Koordinaten und transformierten Zeichenbefehlen. Diese Bereiche zeigen subtile Float-zu-Integer-Unterschiede wahrscheinlicher als einfache Beispiele.
Empfohlener Migrationspfad
Ersetzen Sie zuerst private Konvertierungshelfer, wo möglich, durch klaren Pascal-Code. Behalten Sie zweitens die ursprüngliche 32-Bit-Implementierung nur hinter einer 32-Bit-Bedingung, falls sie noch nötig ist. Fügen Sie drittens einen separaten 64-Bit-Zweig nur hinzu, wenn eine gemessene Performance- oder Binärkompatibilitätsanforderung ihn rechtfertigt. So bleibt der portable Pfad einfach und der architekturspezifische Pfad die Ausnahme statt der Vorgabe.