2011-12-01 100 views
16

我正在研究Cortex-A8和Cortex-A9。我知道一些体系结构不是用整数除法,但除了转换为浮点数,除法,转换为整数之外,最好的方法是什么?或者这确实是最好的解决方案?如何在ARM上进行整数(有符号或无符号)除法?

干杯! =)

+0

当然,即使硬件中不存在,编译器也会支持软件模式下的整数除法。我怀疑那些高规格芯片没有整数除法。我认为ATMega(像Arduino)缺乏它。 – leppie

+5

ARM上的整数除法的汇编指令不存在。 – Phonon

+1

可以将其转换为浮点或用展开的3操作码模式进行手动分割。 –

回答

4

编译器通常包括在其库例如我已经提取他们从GCC除法,gcclib并直接使用它们:

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

要浮起并回可能不是最好的解决方案。你可以尝试一下,看看如何快速的...这是一个乘法,但可以作为很容易使一个除法:

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

我没有时间它虽然看多快/多慢。我明白了我正在使用上面的cortex-m,并且你正在讨论一个cortex-a,谱的不同端,类似的浮点指令和gcc lib的东西是类似的,因为我必须为拇指创建cortex-m,但是可以就像轻松建立手臂一样。实际上,使用gcc它应该都是自动工作的,你不需要像我这样做。其他编译器也不应该像我在上面的冒险游戏中那样做。

7

从其他地方的一些复制面食整数除法: 基本上,每位3指令。来自this的网站,尽管我也见过很多其他地方。 This网站也有一个很好的版本,可能会更快一般。


@ 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) 
+4

这是每个位3个指令,但不是每个位3个周期。每个步骤中的所有指令都立即取决于前一个的标志设置,这意味着取决于内核的结果延迟了3-4个周期。这可能需要每个步骤9-12个周期,总共约360个周期。 –

+0

听起来正确。如果可以摆动它,反向乘法固定点总是更好的选择。 –

2

我写了自己的例程来执行未签名的部门,因为我无法在网上找到未签名的版本。我需要用一个32位的值来分割一个64位的值来得到一个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

10

除以一个恒定值可以找到全部细节是做一个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

+1

这里有舍入错误。对于N = 7的无符号32位除法,我们有n = 2和A = 2454267026.28 ...如果我们取整的A值减小,那么对于“4294967292/7”,结果太小。如果我们把它四舍五入,那么它会给“4294967291/7”带来太大的结果。这只有在A的​​精确值的小数部分小于0.5时才会发生,所以它对于N的大约一半的值(比如3,5或1625)很好。 –

0

我写了下面功能的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或溢出)由绝对值大于或等于除数绝对值的余数表示。有符号划分算法通过分红和除数的符号来区分大小写;它不会首先转换为正整数,因为它不会正确检测所有溢出条件。

相关问题