Pregunta

Estoy trabajando en Cortex-A8 y Cortex-A9 en particular. Sé que algunas arquitecturas no vienen con la división entera, pero ¿cuál es la mejor manera de hacerlo aparte de convertir a flotar, dividir, convertir a entero? ¿O es esa la mejor solución?

¡Salud! =)

¿Fue útil?

Solución

El compilador normalmente incluye una división en su biblioteca, GCCLIB, por ejemplo, los he extraído de GCC y los uso directamente:

https://github.com/dwelch67/stm32vld/ luego stm32f4d/aventura/gcclib

Ir a flotar y de regreso probablemente no sea la mejor solución. Puede probarlo y ver qué tan rápido es ... esto es una multiplica, pero podría convertirlo fácilmente en una división:

https://github.com/dwelch67/stm32vld/ luego stm32f4d/float01/vectores.s

Sin embargo, no lo hice tiempo para ver qué tan rápido/lento. Entendido, estoy usando una Cortex-M arriba y estás hablando de una Cortex-A, diferentes extremos del espectro, instrucciones flotantes similares y las cosas de GCC LIB son similares, para la Cortex-M que tengo que construir para el pulgar pero puedes con la misma facilidad de construir para el brazo. En realidad, con GCC, todo debería funcionar automáticamente, no debería necesitar hacerlo como lo hice. Otros compiladores también no deberías hacerlo de la forma en que lo hice en el juego de aventuras anteriores.

Otros consejos

La división por un valor constante se realiza rápidamente haciendo una multiplicia de 64 bits y un cambio de derecha, por ejemplo, así:

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

Aquí R1 se divide por 1625. El cálculo se realiza así: 64BitReg (R2: R3) = R1*0xa151c331, entonces el resultado es que el superior de 32 bits se despliega por 10:

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

Puede calcular sus propias constantes a partir de esta fórmula:

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

Seleccione la n más grande, para la cual A <2^32

Algunas copias-pasta de otros lugares para una división entera: básicamente, 3 instrucciones por bit. De este Sitio web, aunque también lo he visto en muchos otros lugares.Este El sitio también tiene una buena versión que puede ser más rápida en general.


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

Escribí mi propia rutina para realizar una división sin firmar, ya que no pude encontrar una versión sin firmar en la web. Necesitaba dividir un valor de 64 bits con un valor de 32 bits para obtener un resultado de 32 bits.

El bucle interno no es tan eficiente como la solución firmada proporcionada anteriormente, pero esto admite la aritmética sin firmar. Esta rutina realiza una división de 32 bits si la parte alta del numerador (HI) es menor que el denominador (den), de lo contrario se realiza una división completa de 64 bits (HI: LO/Den). El resultado está en 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

Se puede agregar una verificación adicional para las condiciones de contorno y la potencia de 2. Los detalles completos se pueden encontrar en http://www.idwiz.co.za/tips%20and%20tricks/divide.htm

Escribí las siguientes funciones para el ARM GNU ensamblador. Si no tienes una CPU con udiv/sdiv Soporte de la máquina, simplemente corte las primeras líneas hasta la etiqueta "0:" en cualquier función.

.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

Hay dos funciones, udiv para la división Integer Unsigned y sdiv para la división entera firmada. Ambos esperan un dividendo de 64 bits (firmado o sin firmar) en r1 (Palabra alta) y r0 (palabra baja), y un divisor de 32 bits en r2. Devuelven el cociente en r0 y el resto en r1, por lo tanto, puedes definirlos en un C header como extern Devolver un entero de 64 bits y enmascarar el cociente y el resto después. Un error (división por 0 o desbordamiento) se indica por un resto que tiene un valor absoluto mayor o igual al valor absoluto del divisor. El algoritmo de división firmado usa la distinción del caso por los signos de dividendos y divisores; Primero no se convierte en enteros positivos, ya que eso no detectaría todas las condiciones de desbordamiento correctamente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top