Вопрос по x86, assembly, 64bit, division, numbers – Ассемблер 64б деление

3

Мне нужен какой-то простой способ разделить целые числа без знака 64b в ассемблере для x86. Мой номер сохраняется в двух 32-разрядных регистрах EDX: EAX, и мне нужно вернуть результат в EDX: EAX. Коэффициент в целых 32b. Код, пожалуйста?

@ Брендан, я неЭто означает, что вы можете разбить большее деление на более мелкие (кстати, вы можете в некоторых случаях, если позволяют значения дивидендов и делителей). Я просто предложил повторно использовать тот же базовый алгоритм длинного деления (который реализован в связанном ответе) для больших целых чисел. Alexey Frunze
Если это'В таком случае, это звучит как тыв основном реализуем бинарное деление практически с нуля. Это кажется немного широким для SO вопроса, IMO. что ты уже испробовал? Есть ли более конкретная часть, которую вызастрял на? WeirdlyCheezy
Просто для пояснения, вы имеете в виду с или без использования инструкций x64? То есть, это просто вопрос передачи данных в 64-битные регистры (например, RAX), выполнения 64-битного деления, затем разделения их обратно на 32-битные регистры, или вы пытаетесь эмулировать 64-битное деление на 32-битном процессоре ? WeirdlyCheezy
Если вы действительно делаете 64/64 = 64,64 деления, вы можете расширить код изэтот ответ, Это делает 16/8 = 8,8 деления. Используя пары 32-битных целых чисел для представления 64-битных величин вместо 8-битных и зацикливая 64 раза вместо 8, вы можете превратить этот код в 128/64 = 64,64 с разделением, как 64-битныеdiv в 64-битном режиме. Alexey Frunze

Ваш Ответ

3   ответа
0

числитель / делитель = результат + остаток / делитель

Сначала проверьте, равен ли делитель нулю (отмените, если он есть).

     test eax,eax
     jne .ok
     test edx,edx
     je .divisionByZero
.ok:

Сдвиньте делитель влево, пока не установите MSB, следя за тем, сколько сдвигов вы сделали:

    xor ebp,ebp            ;ebp = bits shifted so far
    test edx,(1 << 31)
    jne .l2
.l1:
    shld edx,eax,1
    shl eax,1
    inc ebp
    test edx,(1 << 31)
    jne .l1
.l2:

Установите текущий результат на ноль:

    xor esi,esi
    xor edi,edi

Теперь переместите делитель обратно в исходное положение; вычитая делитель тока из оставшегося числителя и устанавливая бит в результате, когда делитель тока меньше текущего числителя:

.nextBit:
    shld edi,esi,1
    shl esi,1

    cmp ecx,edx
    jb .doneBit
    ja .subtract
    cmp ebx,eax
    jb .doneBit

.subtract:
    sub ecx,edx
    sbb ebx,eax
    or esi,1

.doneBit:
    sub ebp,1
    jnc .nextBit

На данный момент EDX: EAX - это то же самое значение, что и EDI: ESI - результат, а ECX: EBX - остаток.

ВНИМАНИЕ: Все вышеперечисленное полностью не проверено. Это'Это просто пример / описание.

ПРИМЕЧАНИЕ. Если числа подписаны, сначала необходимо удалить знаковый бит из числителя и делителя; затем установите бит знака в результате и остаток позже (sign = numerator_sign XOR divisor_sign).

5

Factor is in 32b integer), вы хотите разделить 64-битный дивиденд на 32-битный делитель и получить 64-битный коэффициент.

Если это толкование верно, то оноНа самом деле это легко сделать в 32-битном коде.

Идея в том, что вы разделяете обаполовинки» дивидендов делителем и повторно использовать остаток от первого деления для второго деления.

C кодом, иллюстрирующим, как это сделать:

#include <stdio.h>
#include <limits.h>

#define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1]

#if UINT_MAX >= 0xFFFFFFFF
typedef unsigned int uint32;
#else
typedef unsigned long uint32;
#endif
typedef unsigned long long uint64;

typedef unsigned long ulong;

// Make sure uint32=32 bits and uint64=64 bits
C_ASSERT(sizeof(uint32) * CHAR_BIT == 32);
C_ASSERT(sizeof(uint64) * CHAR_BIT == 64);

int div64by32eq64(uint64* dividend, uint32 divisor)
{
  uint32 dividendHi = (uint32)(*dividend >> 32);
  uint32 dividendLo = (uint32)*dividend;
  uint32 quotientHi;
  uint32 quotientLo;

  if (divisor == 0)
    return 0;

  // This can be done as one 32-bit DIV, e.g. "div ecx"
  quotientHi = dividendHi / divisor;
  dividendHi = dividendHi % divisor;

  // This can be done as another 32-bit DIV, e.g. "div ecx"
  quotientLo = (uint32)((((uint64)dividendHi << 32) + dividendLo) / divisor);

  *dividend = ((uint64)quotientHi << 32) + quotientLo;

  return 1;
}

int main(void)
{
  static const struct
  {
    uint64 dividend;
    uint32 divisor;
  } testData[] =
  {
    { 1 , 0 },
    { 0xFFFFFFFFFFFFFFFFULL, 1 },
    { 0xFFFFFFFFFFFFFFFFULL, 2 },
    { 0xFFFFFFFF00000000ULL, 0xFFFFFFFFUL },
    { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFUL },
  };
  int i;

  for (i = 0; i < sizeof(testData)/sizeof(testData[0]); i++)
  {
    uint64 dividend = testData[i].dividend;
    uint32 divisor = testData[i].divisor;

    printf("0x%016llX / 0x%08lX = ", dividend, (ulong)divisor);

    if (div64by32eq64(÷nd, divisor))
      printf("0x%016llX\n", dividend);
    else
      printf("division by 0 error\n");
  }

  return 0;
}
</limits.h></stdio.h>

Выход (ideone):

0x0000000000000001 / 0x00000000 = division by 0 error
0xFFFFFFFFFFFFFFFF / 0x00000001 = 0xFFFFFFFFFFFFFFFF
0xFFFFFFFFFFFFFFFF / 0x00000002 = 0x7FFFFFFFFFFFFFFF
0xFFFFFFFF00000000 / 0xFFFFFFFF = 0x0000000100000000
0xFFFFFFFFFFFFFFFF / 0xFFFFFFFF = 0x0000000100000001

А теперь эквивалентный код деления в сборке (синтаксис NASM) без проверки деления на 0:

; 64-bit dividend
mov edx, 0xFFFFFFFF
mov eax, 0xFFFFFFFF

; 32-bit divisor
mov ecx, 0xFFFFFFFF

push eax
mov eax, edx
xor edx, edx
div ecx ; get high 32 bits of quotient
xchg eax, [esp] ; store them on stack, get low 32 bits of dividend
div ecx ; get low 32 bits of quotient
pop edx ; 64-bit quotient in edx:eax now
; edx:eax should now be equal 0x0000000100000001
Тьфу ... пожалуйста, нет использоватьxchg с операндом памяти, если вы действительно не хотите блокировки шины. fuz
[Решено] .. Вау! За работой! Спасибо, это то, что мне нужно :) ... Спасибовсе вы! Nick
0

ДИВ инструкция?

Ваш делитель тоже 64-битный? Jens Björnhager
Конечно, но если у вас есть один длинный номер, сохраненный в двух регистрах, вы можетет просто использовать один или два "ДИВ», Вам нужно каким-то образом разделить одну часть числа (EDX), вторую часть числа (EAX), объединить результаты и сохранить окончательный результат обратно в EDX: EAX. Я'ищу такой способ, как это сделать. Nick

Похожие вопросы