Деление на константу с использованием сдвигов и сложений / вычитаний - PullRequest
3 голосов
/ 03 февраля 2011

Привет всем, я пытаюсь разделить константу без знака, используя только сдвиги и добавления / вычитания - у меня нет проблем с этим, если бы это было умножение, но я немного озадачен делением.

Например, допустим, что делитель константы равен 192, а дивиденд - 8000

«полный результат» y = 8000/192 = 41 (при условии, что я не сохраняю дробные биты)

y = 8000 >> 8 ... 31 y = 8000 >> 7 ... 62

Но как мне получить более точное решение?

Большое спасибо!

Ответы [ 3 ]

9 голосов
/ 03 февраля 2011

Почти наверняка есть более элегантный способ сделать это, но этого достаточно, чтобы вы начали.

Обычно деление таким способом выполняется путем умножения на обратное значение, то есть сначала умножение, а затем вправоshifting.

(Примечание: помните, что умножение может быть выполнено сдвигами и сложениями (например, n * 3 = (n*2) + (n*1) = (n << 1) + (n) ), но я просто собираюсь использовать умножение здесь. Ваш вопрос сказал «сдвигает и добавляет», и я оправдываюмое сокращенное использование умножения)

В примерах ниже я пытаюсь объяснить концепции на примере. В вашем конкретном случае вы захотите рассмотреть такие вопросы, как

  1. знак (я использую целые числа без знака ниже)

  2. переполнение (ниже я использую 32-битные длинные без знака для хранения промежуточных значений, но если вы напоменьше, остерегайтесь, отрегулируйте соответственно

  3. округление (например, должно ли 9/5 возвращать 1 или 2? В C это 1, но, возможно, вы хотите 2, потому что это ближе к правильному ответу?)

  4. Кв той степени, в которой вы можете (доступные биты), делайте все свои умножения до деления (сводя к минимуму ошибки усечения).Опять же, помните о переполнении.

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

Деление на 192 такое же, какумножение на 1/192, что равно делению на (64 * 3).Не существует точного (конечного) двоичного представления 1/3, поэтому мы приближаем его к 0x5555 / (1 << 16). </p>

Чтобы разделить на 192, мы делим на 64, а затем делим на3. Чтобы разделить на 3, мы умножаем на 0x5555 и смещаем вправо на 16 (или умножаем на 0x55 и >> 8, или ...)

//                8000/192          =
//                ((8000/64)/3)     =
//                ((8000 >> 6) / 3) =
//                (((8000 >> 6) * 0x5555) >> 16)
//                (((8000 * 0x5555) >> 22

Обратите внимание, что круглые скобки являются преднамеренными.Вы не хотите вычислить (8000 * (0x5555/(1 << 16)), потому что 2-й член равен 0, а произведение равно 0. Не хорошо.

Таким образом, 1-строчная строка в коде будет выглядеть примерно так:

 printf("Answer:  %lu\n", ((8000UL * 0x5555UL) >> 22));

Это даст 41, то есть то, что "C" выдаст для 8000/192, даже если 42 равно ".ближе».Проверяя LSB, вы можете округлить, если хотите.

Можно написать трактат на эту тему, но, к счастью, кто-то, намного умнее меня, уже имеет .

4 голосов
/ 08 августа 2011

Я разработал генератор с постоянным делением, который может легко дать вам оптимизированное деление на любую константу.Он следует идеям "Восторга Хакера".

Инструмент с именем "kdiv" доступен на sourceforge:

http://sourceforge.net/projects/kdiv/

2 голосов
/ 03 февраля 2011

Я бы посмотрел книгу восхищения хакеров для такого рода вещей.У меня нет своей копии, но независимо от этого, если вы посмотрите на свой делитель 192, то есть 0xC0, так что вы можете разделить верх и низ на 0x40, смещение 8000 >> 6 = 125. 8000/192 -> 125 /3, но тогда вы должны сделать это делить на 3. Мы знаем, что ответ будет где-то между 125/2 и 125/4.С этими конкретными числами 125 равно 0x7d или b1111101, что в 3 раза больше b100000 + 11101, что (3 раза по 0x20) + (3 раза по 8) + 5, поэтому 125/3 = 0x20 + 0x8 + (5/3) и 5/3 равнобыстро определяется как больше 1, но меньше 2, так что 0x28 + 1 = 41. Сдвиг продолжает уменьшаться только в том случае, если битовая комбинация делителей продолжает появляться в верхних битах битовой комбинации числителя.Я не знаю, что восхищают хакеры или другие подобные источники по этому поводу, я просто случайно заметил этот шаблон для этих конкретных чисел.

...