我正在研发一种没有硬件乘法和除法的微控制器。我需要为这些基本操作制作软件算法,这是紧凑尺寸和效率之间的良好平衡。我的C编译器端口将使用这些算法,而不是C开发人员本身。
我的google-fu到目前为止主要是关于这个主题的噪音。
有人能指点我的信息吗?我可以使用add / sub和shift指令。基于表查找的算法也可能对我有用,但我有点担心编译器的后端这么多......嗯,可以这么说。
答案 0 :(得分:6)
这是一个简单的乘法算法:
从最右边的乘数开始。
如果乘数位为1,则添加被乘数
将被乘数换算为1
移至乘数中的下一位并返回步骤2.
这是一个除法算法:
如果除数大于被除数,请停止。
当除数寄存器小于被除数寄存器时,向左移位。
将除数寄存器右移1。
从被除数寄存器中减去除数寄存器,并将结果寄存器中的位更改为与对除数寄存器进行的移位总数相对应的位。
从第1步开始,将除数寄存器置于原始状态。
当然你需要把支票除以0,但它应该有用。
这些算法当然只适用于整数。
答案 1 :(得分:2)
我最喜欢的参考资料,有书籍形式:
http://www.hackersdelight.org/
你也不能错过TAoCP:http://www-cs-faculty.stanford.edu/~uno/taocp.html
答案 2 :(得分:1)
这是一个除法算法:http://www.prasannatech.net/2009/01/division-without-division-operator_24.html
我认为我们正在谈论整体?
如果没有硬件支持,则必须实现自己的除零异常。
(我很快就找到了一个乘法算法,但我会继续寻找其他人找不到的算法)。
答案 3 :(得分:1)
一个简单且相当高效的整数乘法算法是Russian Peasant Multiplication。
对于有理数,您可以尝试使用二进制quote notation,其分割比平常更容易。
答案 4 :(得分:1)
事实证明,我仍然有一些旧的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
顺便说一句 - 我从未弄清楚是否有必要在开始时将所有内容转换为正数。如果您对换班操作非常小心,那可能是可以避免的开销。
答案 5 :(得分:0)
若要相乘,则将移位的被乘数中的部分乘积添加到累加器,如果设置了乘数中的相应位。在循环结束时移位被乘数和乘数,测试乘数和乘法。 1,看看是否应该加入。
答案 6 :(得分:0)
Microchip PICmicro 16Fxxx系列芯片没有乘法或除法指令。 也许它的一些软乘法和软分频程序可以移植到你的MCU上。
PIC Microcontroller Basic Math Multiplication Methods
PIC Microcontroller Basic Math Division Methods
同时查看"Newton's method" for division。 我认为该方法给出了我见过的任何除法算法的最小可执行大小,尽管这种解释使它听起来比实际上更复杂。 我听说一些早期的Cray超级计算机使用了牛顿的分割方法。