繁体   English   中英

如何在ARM上进行整数(有符号或无符号)除法?

[英]How does one do integer (signed or unsigned) division on ARM?

我正在特别研究Cortex-A8和Cortex-A9。 我知道有些体系结构没有整数除法,但是除了转换为浮点数,除法,转换为整数之外,什么是最好的方法? 还是那确实是最好的解决方案?

干杯! =)

例如,通过进行64位乘和右移,可以快速完成以常数除的运算,如下所示:

LDR     R3, =0xA151C331
UMULL   R3, R2, R1, R3
MOV     R0, R2,LSR#10

这里R1除以1625。计算方法如下:64bitreg(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)

编译器通常在其库中包含一个除法器,例如gcclib,例如,我已从gcc中提取了它们并直接使用它们:

https://github.com/dwelch67/stm32vld/然后stm32f4d / adventure / gcclib

漂浮并返回可能不是最好的解决方案。 您可以尝试一下,看看它有多快...这是一个乘法,但也可以很容易地将其除以除法:

https://github.com/dwelch67/stm32vld/,然后是stm32f4d / float01 / vectors.s

我没有时间,但看看有多快/慢。 理解我在上面使用的是cortex-m,您在谈论的是cortex-a,频谱的不同末端,相似的浮点指令以及gcc lib的内容是相似的,对于cortex-m,我必须为拇指构建,但是您可以就像为手臂轻松打造一样。 实际上,对于gcc来说,它们应该都能自动运行,而无需像我那样做。 其他编译器也不需要像我在上面的冒险游戏中那样做。

我编写了自己的例程来执行未签名的除法,因为我无法在网络上找到未签名的版本。 我需要将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 GNU汇编器编写了以下函数。 如果您没有支持udiv/sdiv机器的CPU,则只需将前两个行的任一行中的“ 0:”标签剪切掉即可。

.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 header中将它们定义为extern返回64位整数,然后屏蔽商和余数。 误差(除以0或溢出)由绝对值大于或等于除数绝对值的余数表示。 有符号除法使用大小写区分除以除数和除数。 它不会首先转换为正整数,因为那样将无法正确检测所有溢出条件。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM