Существует ли DivMod, который *не* ограничивается словами (‹=65535)?

В Delphi объявление функции DivMod выглядит так:

procedure DivMod(Dividend: Cardinal; Divisor: Word;
  var Result, Remainder: Word);

Таким образом, делитель, результат и остаток не могут быть больше 65535, что является довольно жестким ограничением. Почему это? Почему не может быть делекарирование

procedure DivMod(Dividend: Cardinal; Divisor: Cardinal;
  var Result, Remainder: Cardinal);

Процедура реализована на ассемблере и поэтому, вероятно, чрезвычайно быстра. Не было бы возможно для кода

    PUSH    EBX
    MOV     EBX,EDX
    MOV     EDX,EAX
    SHR     EDX,16
    DIV     BX
    MOV     EBX,Remainder
    MOV     [ECX],AX
    MOV     [EBX],DX
    POP     EBX

адаптироваться к кардиналам? Насколько медленнее наивная попытка

procedure DivModInt(const Dividend: integer; const Divisor: integer; out result: integer; out remainder: integer);
begin
  result := Dividend div Divisor;
  remainder := Dividend mod Divisor;
end;

который не (?) ограничен 16-битными целыми числами?


person Andreas Rejbrand    schedule 07.03.2010    source источник
comment
Ответ, который вы приняли, не отвечает на вопрос в заголовке. Возможно, вы могли бы отредактировать заголовок, чтобы он больше соответствовал тому, на что вы действительно хотели получить ответ.   -  person Rob Kennedy    schedule 08.03.2010


Ответы (1)


Возможна такая процедура. Я недостаточно тестировал код, но думаю, что все в порядке:

procedure DivMod32(Dividend, Divisor: Cardinal; var Quotient, Remainder: Cardinal);
asm
        PUSH EBX
        MOV  EBX,EDX
        XOR  EDX,EDX
        DIV  EBX
        MOV  [ECX],EAX
        MOV  EBX,Remainder
        MOV  [EBX],EDX
        POP  EBX
end;

Обновлено:

еще эффективнее:

function DivMod32(Dividend, Divisor: Cardinal; var Remainder: Cardinal): Cardinal;
asm
        PUSH EBX
        MOV  EBX,EDX
        XOR  EDX,EDX
        DIV  EBX
        MOV  [ECX],EDX
        POP  EBX
end;

Обновлено 2:

Вы можете увидеть ассемблерный код, сгенерированный компилятором Delphi, в окне Disassembly (или CPU). Например, процедура

procedure DivMod32(const Dividend: Cardinal; const Divisor: Cardinal;
                    out result: Cardinal; out remainder: Cardinal);
begin
  result := Dividend div Divisor;
  remainder := Dividend mod Divisor;
end;

генерирует код

Unit1.pas.28: begin
0046CC94 55               push ebp
0046CC95 8BEC             mov ebp,esp
0046CC97 53               push ebx
0046CC98 56               push esi
0046CC99 8BF2             mov esi,edx
0046CC9B 8BD8             mov ebx,eax
Unit1.pas.29: result := Dividend div Divisor;
0046CC9D 8BC3             mov eax,ebx
0046CC9F 33D2             xor edx,edx
0046CCA1 F7F6             div esi
0046CCA3 8901             mov [ecx],eax
Unit1.pas.30: remainder := Dividend mod Divisor;
0046CCA5 8BC3             mov eax,ebx
0046CCA7 33D2             xor edx,edx
0046CCA9 F7F6             div esi
0046CCAB 8B4508           mov eax,[ebp+$08]
0046CCAE 8910             mov [eax],edx
Unit1.pas.31: end;
0046CCB0 5E               pop esi
0046CCB1 5B               pop ebx
0046CCB2 5D               pop ebp
0046CCB3 C20400           ret $0004

Этот код является линейным (не содержит переходов), и современные процессоры (с длинным конвейером команд) очень эффективно выполняют линейный код. Итак, хотя моя реализация DivMode32 примерно в 3 раза короче, 60% — разумная оценка.

person kludg    schedule 07.03.2010
comment
Большое тебе спасибо. Ваш код ASM занимает всего ок. 60% времени по сравнению с наивным подходом с использованием операторов div и mod (по крайней мере, в моей системе i7). (Разве это не странно? Разве компилятор Delphi не должен создавать эффективный код?) Как вы думаете, почему RTL предлагает только 16-битную версию DivMod? - person Andreas Rejbrand; 08.03.2010
comment
От компилятора можно ожидать только этого. Серг (аб)использует тот факт, что обе эти строки требуют одинакового деления и что он может получить и частное, и остаток одновременно. Также обратите внимание, что честное сравнение требует, чтобы вы сравнивали с первым, а не со вторым, из-за разницы в сигнатурах методов. Это 8 инструкций в написанном от руки коде ASM по сравнению с 19, созданными Delphi. Удалите 3-4 инструкции для дополнительного деления, и у вас останется 8 вместо 15 инструкций. - person Michael Madsen; 08.03.2010
comment
То, что у вас осталось, в основном отличается из-за другого кода установки/разборки. В D2010 4 из этих инструкций все равно добавлены в его код (манипулирование EBP и RET). Теперь у нас есть преимущество только в 3 инструкции, если вы удалите дополнительное деление из сгенерированного кода - и, насколько я могу судить, разница заключается в том, что Серг выбирает свои регистры немного умнее (2 из которых Серг вырезает, не касаясь ЭСИ вообще). Он может сделать это, потому что он знает назначение вашего кода лучше, чем компилятор — он, вероятно, делает что-то более безопасным способом, потому что не знает, каково ваше намерение. - person Michael Madsen; 08.03.2010
comment
Я сравнил это с процедурой. - person Andreas Rejbrand; 08.03.2010
comment
@Michael: Использование обоих результатов регистра после DIV или IDIV - это то, что, безусловно, ожидается от любого оптимизирующего компилятора. То же самое для хорошего распределения регистров. Компилятор Delphi просто отсутствует в обоих случаях. - person mghie; 08.03.2010
comment
Версию x64 можно найти здесь: stackoverflow.com/questions/20270596/ - person David Heffernan; 28.11.2013