我专门从事Cortex-A8和Cortex-A9的工作。我知道有些架构没有整数除法,但除了转换为浮点数、进行除法运算,再将结果转换为整数外,还有什么更好的办法吗?或者这确实是最好的解决方案?
干杯!=)
我专门从事Cortex-A8和Cortex-A9的工作。我知道有些架构没有整数除法,但除了转换为浮点数、进行除法运算,再将结果转换为整数外,还有什么更好的办法吗?或者这确实是最好的解决方案?
干杯!=)
用一个常数进行除法可以通过进行64位乘法和右移来快速完成,例如:
LDR R3, =0xA151C331
UMULL R3, R2, R1, R3
MOV R0, R2,LSR#10
这里将R1除以1625。计算方法如下:64位寄存器(R2:R3)= R1*0xA151C331,然后将结果的前32位右移10位:
R1*0xA151C331/2^(32+10) = R1*0.00061538461545751488 = R1/1624.99999980
你可以使用这个公式计算自己的常数:
x / N == (x*A)/2^(32+n) --> A = 2^(32+n)/N
选择最大的n,使得A < 2^32。
以下是有关整数除法的一些复制粘贴内容: 基本上,每位需要3条指令。此信息来自此网站,虽然我也在许多其他地方看到过。 这个网站还有一个很好的版本,可能更快。
@ Entry r0: numerator (lo) must be signed positive
@ r2: deniminator (den) must be non-zero and signed negative
idiv:
lo .req r0; hi .req r1; den .req r2
mov hi, #0 @ hi = 0
adds lo, lo, lo
.rept 32 @ repeat 32 times
adcs hi, den, hi, lsl #1
subcc hi, hi, den
adcs lo, lo, lo
.endr
mov pc, lr @ return
@ Exit r0: quotient (lo)
@ r1: remainder (hi)
由于我在网络上找不到无符号版本,因此我编写了自己的无符号除法程序。我需要对64位值进行32位除法以得到32位结果。
内部循环与上面提供的有符号解决方案效率不同,但这确实支持无符号算术运算。如果分子(hi)的高部分小于分母(den),则此例程执行32位除法;否则执行完整的64位除法(hi:lo/den)。结 果在lo中。
cmp hi, den // if hi < den do 32 bits, else 64 bits
bpl do64bits
REPT 32
adds lo, lo, lo // shift numerator through carry
adcs hi, hi, hi
subscc work, hi, den // if carry not set, compare
subcs hi, hi, den // if carry set, subtract
addcs lo, lo, #1 // if carry set, and 1 to quotient
ENDR
mov r0, lo // move result into R0
mov pc, lr // return
do64bits:
mov top, #0
REPT 64
adds lo, lo, lo // shift numerator through carry
adcs hi, hi, hi
adcs top, top, top
subscc work, top, den // if carry not set, compare
subcs top, top, den // if carry set, subtract
addcs lo, lo, #1 // if carry set, and 1 to quotient
ENDR
mov r0, lo // move result into R0
mov pc, lr // return
可以添加额外的边界条件检查和2的幂次方。完整的详细信息可以在http://www.idwiz.co.za/Tips%20and%20Tricks/Divide.htm找到。
.arm
.cpu cortex-a7
.syntax unified
.type udiv,%function
.globl udiv
udiv: tst r1,r1
bne 0f
udiv r3,r0,r2
mls r1,r2,r3,r0
mov r0,r3
bx lr
0: cmp r1,r2
movhs r1,r2
bxhs lr
mvn r3,0
1: adds r0,r0
adcs r1,r1
cmpcc r1,r2
subcs r1,r2
orrcs r0,1
lsls r3,1
bne 1b
bx lr
.size udiv,.-udiv
.type sdiv,%function
.globl sdiv
sdiv: teq r1,r0,ASR 31
bne 0f
sdiv r3,r0,r2
mls r1,r2,r3,r0
mov r0,r3
bx lr
0: mov r3,2
adds r0,r0
and r3,r3,r1,LSR 30
adcs r1,r1
orr r3,r3,r2,LSR 31
movvs r1,r2
ldrvc pc,[pc,r3,LSL 2]
bx lr
.int 1f
.int 3f
.int 5f
.int 11f
1: cmp r1,r2
movge r1,r2
bxge lr
mvn r3,1
2: adds r0,r0
adcs r1,r1
cmpvc r1,r2
subge r1,r2
orrge r0,1
lsls r3,1
bne 2b
bx lr
3: cmn r1,r2
movge r1,r2
bxge lr
mvn r3,1
4: adds r0,r0
adcs r1,r1
cmnvc r1,r2
addge r1,r2
orrge r0,1
lsls r3,1
bne 4b
rsb r0,0
bx lr
5: cmn r1,r2
blt 6f
tsteq r0,r0
bne 7f
6: mov r1,r2
bx lr
7: mvn r3,1
8: adds r0,r0
adcs r1,r1
cmnvc r1,r2
blt 9f
tsteq r0,r3
bne 10f
9: add r1,r2
orr r0,1
10: lsls r3,1
bne 8b
rsb r0,0
bx lr
11: cmp r1,r2
blt 12f
tsteq r0,r0
bne 13f
12: mov r1,r2
bx lr
13: mvn r3,1
14: adds r0,r0
adcs r1,r1
cmpvc r1,r2
blt 15f
tsteq r0,r3
bne 16f
15: sub r1,r2
orr r0,1
16: lsls r3,1
bne 14b
bx lr
有两个函数,udiv
用于无符号整数除法,sdiv
用于有符号整数除法。它们都期望在 r1
(高位)和 r0
(低位)中包含一个64位被除数(有符号或无符号),以及在 r2
中包含一个32位除数。它们将商存放在 r0
中,余数存放在 r1
中,因此你可以在 C 头文件中定义它们为返回64位整数的 extern
,然后在之后掩码处理商和余数。当余数的绝对值大于或等于除数的绝对值时,会指示出现错误(除以0或溢出)。有符号除法算法通过区分被除数和除数的符号来实现;它不会先转换为正整数,因为这样无法正确检测所有溢出条件。
SDIV
或UDIV
吗?Cortex-A8是ARMv7,但是根据http://infocenter.arm.com/help/topic/com.arm.doc.qrc0001m/QRC0001_UAL.pdf的说法,只有一些处理器被支持。 - leppie