Типичным способом является сдвиг и вычитание.Это в основном очень похоже на длинное деление, как мы узнали в школе.Большая разница в том, что в десятичном делении вам нужно оценить следующую цифру результата.В двоичном коде это тривиально.Следующая цифра всегда либо 0, либо 1. Если делитель (смещенный влево) меньше или равен текущему значению дивиденда, вы вычитаете его, а текущий бит результата равен 1. Если он больше, тотекущий бит результата равен 0. Код выглядит следующим образом:
unsigned divide(unsigned dividend, unsigned divisor) {
unsigned denom=divisor;
unsigned current = 1;
unsigned answer=0;
if ( denom > dividend)
return 0;
if ( denom == dividend)
return 1;
while (denom <= dividend) {
denom <<= 1;
current <<= 1;
}
denom >>= 1;
current >>= 1;
while (current!=0) {
if ( dividend >= denom) {
dividend -= denom;
answer |= current;
}
current >>= 1;
denom >>= 1;
}
return answer;
}
Это работает почти так же, как когда мы делаем длинное деление вручную.Например, давайте рассмотрим 972/5.В десятичном делении на длинные мы делаем что-то вроде этого:
____
5)972
Затем мы вычисляем каждую цифру отдельно.5 входит в 9 один раз, поэтому мы записываем 1 в этой цифре ответа и вычитаем 1 * 5 из (этой цифры) дивиденда, а затем "понижаем" следующую цифру дивиденда:
1
----
5)972
5
---
47
Мы продолжаем делать то же самое, пока не введем все цифры:
194
----
5)972
5
---
47
45
---
22
20
---
2
Итак, наш ответ 194 остаток 2.
Теперь давайте рассмотрим то же самое,но в двоичном.972 в двоичном виде - 11 1100 1100
, а 5 - 101
.Теперь есть одно принципиальное различие между делением в двоичном и десятичном виде: в десятичной дроби конкретная цифра может быть от 0 до 9, поэтому нам пришлось умножить, чтобы найти промежуточный результат, который мы собирались вычесть из дивиденда.В двоичном виде цифра всегда будет 0 или 1. Нам никогда не нужно умножать, потому что мы умножаем только на 0 или 1 (что мы обычно обрабатываем в операторе if - либо мы вычитаем, либо нет).
-----------
101)1111001100
Итак, наш первый шаг - выяснить, какая цифра будет первой в результате.Мы делаем это, сравнивая 101 с 1111001100 и сдвигая его влево, пока он не станет больше.Это дает нам:
|
1111001100
10100000000
По мере того, как мы делаем это смещение, мы подсчитываем количество смещенных мест, чтобы мы знали, какую цифру результата мы заполняем в любой момент времени.Я показал это с вертикальной чертой выше.Затем мы сдвигаем промежуточный результат вправо на одно место и сдвигаем вертикальную черту вправо с ним, чтобы указать, где мы делаем, чтобы заполнить цифру результата:
|
1111001100
1010000000
Оттуда мы проверяем, является ли сдвинутый делительменьше дивидендов.Если это так, мы заполняем 1 в правильном месте в ответе и вычитаем сдвинутый делитель из промежуточного результата [и чтобы помочь сохранить столбцы прямыми, я собираюсь вставить несколько пробелов]:
1
-----------------------------
101)1 1 1 1 0 0 1 1 0 0
1 0 1 0 0 0 0 0 0 0
----------------------------
1 0 1
Мы продолжаем в том же духе, заполняя цифры результата и вычитая сдвинутый делитель из промежуточного результата, пока мы не заполним все цифры.В дальнейших попытках помочь разобраться, я собираюсь написать каждую цифру результата в крайнем правом углу рядом с вычитаемым:
1 1 0 0 0 0 1 0
-----------------------------
101)1 1 1 1 0 0 1 1 0 0
1 0 1 1
-----------------------------
1 0 1
1 0 1 1
-----------------------------
0 0 0 0
--------------------------
0 0 0 0
-------------------------
0 0 1 0
-------------------------
0 1 1 0
-------------------------
1 1 0
1 0 1 1
------------------------
0 1 0 0
Итак, мы получаем результат 11000010, остаток10. Преобразовав их в десятичную, мы получаем ожидаемые 194 и 2. соответственно.
Давайте рассмотрим, как это относится к приведенному выше коду.Мы начинаем со смещения делителя влево, пока он не станет больше, чем дивиденд.Затем мы многократно сдвигаем его вправо и для каждого сдвига вправо проверяем, меньше ли это значение, чем промежуточное значение, которое мы получили после последнего вычитания.Если оно меньше, мы снова вычитаем и заполняем 1
для этой цифры в нашем результате.Если оно больше, мы «вычитаем 0» (ничего не делаем) и заполняем «0» для этой цифры в результате (что, опять же, не требует от нас ничего делать, поскольку эти цифры уже установлены в0's).
Когда мы заполнили все цифры, это наш результат, и любая оставшаяся сумма, которую мы еще не вычли, является нашим остатком.
Некоторые спрашивают, почему я использовал|=
вместо +=
в коде.Я надеюсь, что это помогает объяснить, почему.Хотя в этом случае они дают одинаковый результат, я не думаю о добавлении каждой цифры к существующему частичному ответу.Скорее, я думаю, что это место в ответе пустое, и or
просто заполняет его.