Старый код Delphi иногда содержал небольшой помощник _ftol для преобразования значения с плавающей точкой в целое. В 32-битных проектах такие помощники часто писали на inline assembler, особенно в графическом и 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-битной цели. 64-битный компилятор Delphi сообщает E1025 Unsupported language feature: 'ASM' потому что DCC64 не поддерживает смешивание Pascal и inline assembly внутри обычных Pascal-процедур.
Почему 64-битный компилятор отклоняет это
DCC64 может ассемблировать 64-битные assembly-процедуры, но процедура должна быть написана как полноценная assembler routine с учетом 64-битного соглашения о вызовах. Нельзя скопировать 32-битный inline assembler в тело Pascal-функции и ожидать, что компилятор переведет использование регистров, раскладку стека или поведение x87.
Полная assembler-версия возможна, и следующий код показывает обработку control-word, которая может понадобиться, когда значение уже находится в FPU stack.
|
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, если нужна только усеченная конвертация
Для большинства прикладного кода assembler-процедура не нужна. Если реальное требование — усечь Double к нулю и вернуть целое значение, ясная и переносимая версия выглядит просто так:
|
1 2 3 4 |
function _ftol(f: double): integer; cdecl; begin Result := Trunc(f); end; |
Trunc прямо выражает намерение, компилируется для 32- и 64-битных целей и избегает затрат на сопровождение architecture-specific assembler. Будущим сопровождающим также проще рассуждать о проверке диапазона и переполнении.
Рекомендации по миграции
- Используйте реализацию на Pascal, когда вы контролируете место вызова и нужна только числовая конвертация.
- Оставляйте assembler export только тогда, когда двоичная совместимость требует конкретный символ и соглашение о вызовах.
- Не копируйте 32-битный код регистров в 64-битную процедуру без проверки соглашения о вызовах и ширины данных.
- Тестируйте пограничные случаи: отрицательные значения, большие значения и значения вне диапазона целевого целого типа.
Диапазон и поведение
Замена assembler на Trunc все равно должна считаться изменением поведения, требующим тестов. Проверьте, как код должен вести себя с отрицательными числами, дробями около целой границы и значениями вне диапазона Integer. Старый assembler мог зависеть от состояния FPU control-word, тогда как версия Pascal следует реализации RTL целевого компилятора.
Если помощник находится внутри PDF- или графического компонента, протестируйте вывод с повернутым текстом, масштабированными координатами и трансформированными командами рисования. Эти области чаще выявляют тонкие различия float-to-integer, чем простые примеры.
Рекомендуемый путь миграции
Сначала замените приватные помощники конвертации ясным кодом Pascal там, где это возможно. Затем оставьте исходную 32-битную реализацию только за 32-битным условием, если она все еще нужна. Наконец, добавляйте отдельную 64-битную ветку только при измеренном требовании производительности или двоичной совместимости. Так переносимый путь остается простым, а architecture-specific путь становится исключением, а не умолчанием.