Question

Je travaille sur Cortex-A8 et Cortex-A9 en particulier. Je sais que certaines architectures ne viennent pas avec la division entière, mais quelle est la meilleure façon de le faire autre que converti à flotteur, diviser, convertir en entier? Ou est-ce bien la meilleure solution?

Vive! =)

Était-ce utile?

La solution

Le compilateur comprend normalement une division dans sa bibliothèque, gcclib par exemple, je les ai extrait de gcc et de les utiliser directement:

https://github.com/dwelch67/stm32vld/ puis stm32f4d / aventure / gcclib

va flotter et probablement pas en arrière la meilleure solution. vous pouvez l'essayer et de voir à quelle vitesse il est ... Ceci est un make se multiplient, mais pourrait aussi facilement une fracture:

https://github.com/dwelch67/stm32vld/ puis stm32f4d / float01 / vecteurs. s

Je na pas le temps, il se voir à quelle vitesse / lent. ENTENDU J'utilise un cortex m au-dessus et vous parlez d'un cortex a, différentes extrémités du spectre, des instructions de flotteur similaires et les choses lib gcc est similaire, pour le cortex m je dois construire pour le pouce, mais vous pouvez tout comme construire facilement pour le bras. En fait, avec gcc il devrait tout simplement travailler automagiquement vous ne devriez pas besoin de le faire comme je l'ai fait. D'autres compilateurs ainsi vous ne devriez pas avoir besoin de le faire comme je l'ai fait dans le jeu d'aventure ci-dessus.

Autres conseils

Division par une valeur constante se fait rapidement en faisant une 64bit-multiplier et de décalage à droite, par exemple, comme ceci:

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

ici R1 est divisée par 1625. Le calcul se fait comme suit: 64bitreg (R2: R3) = R1 * 0xA151C331, alors le résultat est le 32 bits en haut à droite décalée de 10:

R1*0xA151C331/2^(32+10) = R1*0.00061538461545751488 = R1/1624.99999980

Vous pouvez calculer vos propres constantes de cette formule:

x / N ==  (x*A)/2^(32+n)   -->       A = 2^(32+n)/N

sélectionner le plus grand n, pour lesquels A <2 ^ 32

Certains copie pâtes d'ailleurs pour une division entière: En gros, 3 instructions par bit. De ce site , bien que je l'ai vu beaucoup d'autres endroits. Ce site dispose également d'une version agréable qui peut être plus rapide en général .


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

J'ai écrit ma propre routine pour effectuer une division non signée que je ne pouvais pas trouver une version non signée sur le web. Je avais besoin de diviser une valeur 64 bits avec une valeur de 32 bits pour obtenir un résultat de 32 bits.

La boucle intérieure n'est pas aussi efficace que la solution signée fournie ci-dessus, mais pris en charge par l'arithmétique non signé. Cette routine exécute une division de 32 bits si la partie haute du numérateur (salut) est plus petit que le dénominateur (den), autrement une pleine division de 64 bits est effectué (salut: lo / den). Le résultat est 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

vérification supplémentaire pour les conditions aux limites et puissance de 2 peut être ajouté. Vous trouverez tous les détails http://www.idwiz.co.za /Tips%20and%20Tricks/Divide.htm

J'ai écrit les fonctions suivantes pour l'assembleur ARM GNU. Si vous ne disposez pas d'une unité centrale de traitement avec le soutien de la machine à udiv/sdiv, il suffit de couper les premières lignes à la « 0 ». Étiquette soit fonction

.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

Il y a deux fonctions, udiv pour la division entier non signé et sdiv pour la division entière signée. Ils comptent tous deux un dividende de 64 bits (soit signé ou non signé) dans r1 (mot haut) et r0 (mot faible), et un diviseur 32 bits dans r2. Ils reviennent le quotient dans r0 et le reste dans r1, ainsi vous pouvez les définir dans un C header comme extern retournant un entier de 64 bits et masquer le quotient et le reste par la suite. Une erreur (division par 0 ou de débordement) est indiqué par un reste ayant une valeur absolue supérieure ou égale à la valeur absolue du diviseur. L'algorithme de division signé utilise la distinction de cas par les signes des deux dividende et le diviseur; il ne convertit pas en nombres entiers positifs d'abord, car cela ne détecte pas toutes les conditions de débordement correctement.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top