2011-12-01 137 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或溢出)由絕對值大於或等於除數絕對值的餘數表示。有符號劃分算法通過分紅和除數的符號來區分大小寫;它不會首先轉換爲正整數,因爲它不會正確檢測所有溢出條件。

相關問題