Где я могу найти алгоритмы мягкого умножения и деления?

Я работаю над микроконтроллером без аппаратного умножения и деления. Мне нужно разработать программные алгоритмы для этих основных операций, которые представляют собой хороший баланс компактного размера и эффективности. Мой порт компилятора C будет использовать эти алгоритмы, а не сами разработчики C.

Мой гугл-фу до сих пор выдает в основном шум по этой теме.

Может ли кто-нибудь указать мне что-то информативное? Я могу использовать инструкции добавления/подписки и сдвига. Алгоритмы, основанные на поиске по таблицам, также могут работать для меня, но я немного беспокоюсь о том, чтобы втиснуть так много во внутреннюю часть компилятора ... гм, так сказать.


person srking    schedule 05.03.2010    source источник
comment
Это какой-то странный микроконтроллер, для которого еще нет компилятора C? Вы можете либо использовать этот компилятор (вероятно, хорошая идея), либо проверить его исходный код, если он доступен, для подпрограмм.   -  person Carl Norum    schedule 06.03.2010
comment
У этого микроконтроллера есть имя?   -  person AakashM    schedule 06.03.2010
comment
Это новая. Это часть исследовательского проекта.   -  person srking    schedule 06.03.2010
comment
@srking, посмотрите на некоторые внутренности gcc и binutils, чтобы узнать, что они выводят для архитектур без аппаратного умножения / деления. Вы должны быть в состоянии адаптировать что-то легко.   -  person Carl Norum    schedule 06.03.2010
comment
@Carl В частности, OP следует взглянуть на libgcc, где фактически реализовано мягкое умножение / деление GCC. gcc.gnu.org/onlinedocs/gccint/Integer-library-routines. html   -  person ephemient    schedule 06.03.2010
comment
+1 за упоминание libgcc, но код очень трудно читать.   -  person Richard Pennington    schedule 06.03.2010
comment
Да, некоторые примеры сборки с псевдокодом для контроллера дуги находятся здесь: gcc-4.4.2/gcc/config/arc/lib1funcs.asm. Они используют алгоритм Джастина ниже.   -  person srking    schedule 06.03.2010


Ответы (7)


Вот простой алгоритм умножения:

  1. Начните с крайнего правого бита множителя.

  2. Если бит в множителе равен 1, добавить множимое

  3. Сдвиг множителя на 1

  4. Перейдите к следующему биту в множителе и вернитесь к шагу 2.

А вот и алгоритм деления:

  1. Если делитель больше делимого, остановитесь.

  2. Пока регистр делителя меньше регистра делимого, сдвиг влево.

  3. Сдвинуть регистр делителя вправо на 1.

  4. Вычтите регистр делителя из регистра делимого и измените бит на 1 в регистре результата на бит, который соответствует общему количеству сдвигов, сделанных в регистре делителя.

  5. Начните с шага 1 с регистром делителя в исходном состоянии.

Конечно, вам нужно поставить галочку на деление на 0, но она должна работать.

Эти алгоритмы, конечно, только для целых чисел.

person Justin Peel    schedule 05.03.2010
comment
Что произойдет на шаге 3 алгоритма деления, если вы разделите (1‹‹n)+1 на (1‹‹x), где n — ширина регистра-1? - person ; 01.06.2010
comment
@jbcreix, я не уверен, что понимаю, о чем ты спрашиваешь. Можете ли вы уточнить? - person Justin Peel; 01.06.2010
comment
если вы сдвинете делитель влево, в то время как делимое больше, старший бит будет смещен в очень больших делимых. Итак, когда вы сдвинетесь вправо, вы получите другое число в случае делителя степени 2, 0. - person ; 01.06.2010
comment
@jbcreix, да, конечно, вы должны быть осторожны, чтобы избежать проблем с переполнением. - person Justin Peel; 01.06.2010

Мой любимый справочник по подобным вещам, доступный в виде книги:

http://www.hackersdelight.org/

Также вы не ошибетесь с TAoCP: http://www-cs-faculty.stanford.edu/~uno/taocp.html

person Johan Kotlinski    schedule 05.03.2010

Вот алгоритм деления: http://www.prasannatech.net/2009/01/division-without-division-operator_24.html

Я полагаю, мы говорим о целых числах?

Если нет аппаратной поддержки, вам придется реализовать собственное исключение деления на ноль.

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

person Seth    schedule 05.03.2010
comment
В Википедии описано множество алгоритмов умножения, самый простой из которых — сдвиг и сложение. en.wikipedia.org/wiki/Multiplication_algorithm#Shift_and_add - person Carl Norum; 06.03.2010

Одним из простых и достаточно эффективных алгоритмов умножения целых чисел является русское крестьянское умножение.

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

person outis    schedule 05.03.2010
comment
Я думал, что алгоритм для людей, а не для машин. - person Steve314; 06.03.2010
comment
Алгоритмы не зависят от процессора. - person outis; 06.03.2010
comment
Не обязательно — один алгоритм может работать лучше или хуже других в зависимости от того, поддерживает ли оборудование, на котором он работает, правильные основные операции. Человеческий мозг поддерживает различные базовые операции для процессора. Мой личный мозг складывает 32-битные целые числа, по крайней мере, в несколько миллиардов раз медленнее, чем, например, x86, тогда как у меня есть аппаратно-ускоренные алгоритмы зрения, которые намного превосходят любой современный ПК. - person Steve314; 06.03.2010
comment
Что ж, поскольку разделить на 2 и отбросить остаток — это просто сдвиг вправо, а проверка на нечетность логична, а с 1 и проверка против 0, похоже, это довольно хорошо сопоставляется с основными операциями ЦП. По сути, это тот же алгоритм, который описан в ответе Джастина. - person caf; 06.03.2010
comment
@ Steve314: вы правы в том, что эффективность алгоритма зависит от эффективности основных операций, которые зависят от процессора, но правильность алгоритма не зависит от процессора. В этом смысле крестьянское умножение не предназначено для какого-то конкретного процессора. Как указывает caf, крестьянское умножение эффективно на процессорах. - person outis; 06.03.2010
comment
@caf: ага. Все конкретные алгоритмы умножения, опубликованные здесь в качестве ответов, по сути одинаковы. - person outis; 06.03.2010

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

У 68000 были инструкции умножения и деления IIRC - я думаю, что они были написаны в качестве учебного упражнения.

Решил просто разместить код здесь. Добавлены комментарии и, в процессе, исправлена ​​проблема.

;
; Purpose  : division of longword by longword to give longword
;          : all values signed.
; Requires : d0.L == Value to divide
;          : d1.L == Value to divide by
; Changes  : d0.L == Remainder
;          : d2.L == Result
;          : corrupts d1, d3, d4
;

        section text

ldiv:   move    #0,d3     ; Convert d0 -ve to +ve - d3 records original sign
        tst.l   d0
        bpl.s   lib5a
        neg.l   d0
        not     d3
lib5a:  tst.l   d1        ; Convert d1 -ve to +ve - d3 records result sign
        bpl.s   lib5b
        neg.l   d1
        not     d3
lib5b:  tst.l   d1        ; Detect division by zero (not really handled well)
        bne.s   lib3a
        rts
lib3a:  moveq.l #0,d2     ; Init working result d2
        moveq.l #1,d4     ; Init d4
lib3b:  cmp.l   d0,d1     ; while d0 < d1 {
        bhi.s   lib3c
        asl.l   #1,d1     ; double d1 and d4
        asl.l   #1,d4
        bra.s   lib3b     ; }
lib3c:  asr.l   #1,d1     ; halve d1 and d4
        asr.l   #1,d4
        bcs.s   lib3d     ; stop when d4 reaches zero
        cmp.l   d0,d1     ; do subtraction if appropriate
        bhi.s   lib3c
        or.l    d4,d2     ; update result
        sub.l   d1,d0
        bne.s   lib3c
lib3d:                    ; fix the result and remainder signs
;       and.l   #$7fffffff,d2  ; don't know why this is here
        tst     d3
        beq.s   lib3e
        neg.l   d2
        neg.l   d0
lib3e:  rts

;
; Purpose  : Multiply long by long to give long
; Requires : D0.L == Input 1
;          : D1.L == Input 2
; Changes  : D2.L == Result
;          : D3.L is corrupted
;

lmul:   move    #0,d3       ; d0 -ve to +ve, original sign in d3
        tst.l   d0
        bpl.s   lib4c
        neg.l   d0
        not     d3
lib4c:  tst.l   d1          ; d1 -ve to +ve, result sign in d3
        bpl.s   lib4d
        neg.l   d1
        not     d3
lib4d:  moveq.l #0,d2       ; init d2 as working result
lib4a:  asr.l   #1,d0       ; shift d0 right
        bcs.s   lib4b       ; if a bit fell off, update result
        asl.l   #1,d1       ; either way, shift left d1
        tst.l   d0
        bne.s   lib4a       ; if d0 non-zero, continue
        tst.l   d3          ; basically done - apply sign?
        beq.s   lib4e       ; was broken! now fixed
        neg.l   d2
lib4e:  rts
lib4b:  add.l   d1,d2      ; main loop body - update result
        asl.l   #1,d1
        bra.s   lib4a

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

person Steve314    schedule 05.03.2010

Чтобы умножить, добавьте частичные произведения из сдвинутого множимого в аккумулятор, если установлен соответствующий бит в множителе. Сдвиньте множитель и множитель в конце цикла, проверяя множитель и 1, чтобы увидеть, нужно ли делать сложение.

person Arthur Kalliokoski    schedule 05.03.2010

Микросхемы Microchip серии PICmicro 16Fxxx не имеют инструкции умножения или деления. Возможно, некоторые из процедур мягкого умножения и мягкого деления для него можно портировать на ваш MCU.

Основные математические методы умножения микроконтроллера PIC

Основные математические методы деления микроконтроллера PIC

Также ознакомьтесь с "Метод Ньютона" для деления. Я думаю, что этот метод дает наименьший размер исполняемого файла из всех алгоритмов деления, которые я когда-либо видел, хотя объяснение делает его более сложным, чем он есть на самом деле. Я слышал, что некоторые ранние суперкомпьютеры Cray использовали для деления метод Ньютона.

person Community    schedule 01.06.2010