Un ancien code Delphi incluait parfois un petit helper _ftol pour convertir une valeur à virgule flottante en entier. Dans les projets 32 bits, il était courant d’écrire ce type de helper en assembleur inline, surtout dans le code graphique et PDF optimisé pour d’anciens compilateurs.
|
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; |
Cette approche casse lorsque la même unité est compilée pour une cible 64 bits. Le compilateur Delphi 64 bits signale E1025 Unsupported language feature: 'ASM', car DCC64 ne prend pas en charge le mélange Pascal et assembleur inline dans des routines Pascal normales.
Pourquoi le compilateur 64 bits le rejette
DCC64 peut assembler des routines 64 bits, mais la routine doit être écrite comme une routine assembleur complète en tenant compte de la convention d’appel 64 bits. Vous ne pouvez pas copier de l’assembleur inline 32 bits dans le corps d’une fonction Pascal et attendre du compilateur qu’il traduise l’usage des registres, la disposition de pile ou le comportement x87.
Une version assembleur complète est possible, et le code suivant montre le type de gestion du mot de contrôle qui peut être requis lorsque la valeur est déjà sur la pile 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; |
Préférer Pascal lorsque le but est seulement de tronquer
Pour la plupart du code applicatif, la routine assembleur est inutile. Si le vrai besoin est de tronquer un Double vers zéro et de retourner un entier, la version claire et portable est simplement:
|
1 2 3 4 |
function _ftol(f: double): integer; cdecl; begin Result := Trunc(f); end; |
Trunc exprime directement l’intention, compile pour les cibles 32 et 64 bits, et évite le coût de maintenance d’un assembleur propre à l’architecture. Il est aussi plus facile pour les futurs mainteneurs de raisonner sur les contrôles de plage et le comportement de débordement.
Conseils de migration
- Utilisez l’implémentation Pascal lorsque vous contrôlez le site d’appel et n’avez besoin que d’une conversion numérique.
- Conservez un export assembleur uniquement lorsque la compatibilité binaire exige un symbole et une convention d’appel précis.
- Ne copiez pas du code registre 32 bits dans une routine 64 bits sans revoir la convention d’appel et la largeur des données.
- Testez les cas limites comme les valeurs négatives, les grandes valeurs et les valeurs hors de la plage de l’entier cible.
Considérations de plage et de comportement
Remplacer l’assembleur par Trunc doit tout de même être traité comme un changement de comportement méritant des tests. Confirmez le comportement attendu pour les nombres négatifs, les fractions proches d’une limite entière et les valeurs hors de la plage de Integer. L’ancien assembleur pouvait dépendre de l’état du mot de contrôle FPU, tandis que la version Pascal suit l’implémentation RTL du compilateur cible.
Si le helper se trouve dans un composant PDF ou graphique, testez les sorties utilisant du texte pivoté, des coordonnées mises à l’échelle et des commandes de dessin transformées. Ces zones révèlent plus souvent de subtiles différences de conversion flottant-entier que les exemples simples.
Chemin de migration recommandé
Remplacez d’abord les helpers privés de conversion par du code Pascal clair lorsque c’est possible. Ensuite, conservez l’implémentation 32 bits originale seulement derrière une condition 32 bits si elle reste nécessaire. Enfin, ajoutez une branche 64 bits séparée uniquement lorsqu’une exigence mesurée de performance ou de compatibilité binaire le justifie. Le chemin portable reste ainsi simple, et le chemin propre à l’architecture devient l’exception plutôt que la valeur par défaut.