Процедура деления целых чисел, которая использует сдвиги и добавления, может быть получена простым способом из десятичного деления на длинные руки, как учили в начальной школе. Выбор каждой условной цифры упрощается, так как цифра равна 0 и 1: если текущий остаток больше или равен делителю, младший значащий бит частичного частного равен 1.
Просто как и с децимальным делением на длинные руки, цифры дивиденда считаются от наиболее значимых до наименее значимых, по одной цифре за раз. Это легко осуществить левым сдвигом в двоичном делении. Кроме того, частные биты собираются левым смещением текущих битов отношения на одну позицию, а затем добавлением нового бит частного.
В классическом расположении эти два сдвига влево объединены в левое смещение одной пары регистров. Верхняя половина удерживает текущий остаток, нижняя половина начинается с дивиденда. Поскольку биты дивидендов передаются в регистр остатка по левому сдвигу, неиспользуемые младшие значащие разряды нижней половины используются для накопления частных битов.
Ниже приведен язык ассемблера x86 и реализации C этого алгоритма. Этот конкретный вариант сдвига & amp; добавление деления иногда упоминается как «невыполняющий» вариант, так как вычитание делителя из текущего остатка не выполняется, если остаток больше или равен делителю. В C нет понятия флага переноса, используемого версией сборки в левом сдвиге пары регистров. Вместо этого он эмулируется, основываясь на наблюдении, что результат добавления по модулю 2n может быть меньшим, чем либо добавляться только в случае выполнения.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define USE_ASM 0
#if USE_ASM
uint32_t bitwise_division (uint32_t dividend, uint32_t divisor)
{
uint32_t quot;
__asm {
mov eax, [dividend];// quot = dividend
mov ecx, [divisor]; // divisor
mov edx, 32; // bits_left
mov ebx, 0; // rem
$div_loop:
add eax, eax; // (rem:quot) << 1
adc ebx, ebx; // ...
cmp ebx, ecx; // rem >= divisor ?
jb $quot_bit_is_0; // if (rem < divisor)
$quot_bit_is_1: //
sub ebx, ecx; // rem = rem - divisor
add eax, 1; // quot++
$quot_bit_is_0:
dec edx; // bits_left--
jnz $div_loop; // while (bits_left)
mov [quot], eax; // quot
}
return quot;
}
#else
uint32_t bitwise_division (uint32_t dividend, uint32_t divisor)
{
uint32_t quot, rem, t;
int bits_left = CHAR_BIT * sizeof (uint32_t);
quot = dividend;
rem = 0;
do {
// (rem:quot) << 1
t = quot;
quot = quot + quot;
rem = rem + rem + (quot < t);
if (rem >= divisor) {
rem = rem - divisor;
quot = quot + 1;
}
bits_left--;
} while (bits_left);
return quot;
}
#endif