Как выполнить целочисленное (знаковое или беззнаковое) деление в ARM?

StackOverflow https://stackoverflow.com/questions/8348030

Вопрос

Я работаю, в частности, над Cortex-A8 и Cortex-A9.Я знаю, что некоторые архитектуры не поддерживают целочисленное деление, но каков наилучший способ сделать это, кроме преобразования в float, divide, convert в integer?Или это действительно лучшее решение?

Ваше здоровье!= )

Это было полезно?

Решение

Компилятор обычно включает в себя разрыв в своей библиотеке, GCClib, например, я извлек их из GCC и использую их напрямую:

https://github.com/dwelch67/stm32vld/ затем STM32F4D/Adventure/GCClib

Собираться плавать и обратно, вероятно, не лучшее решение. Вы можете попробовать это и посмотреть, как быстро это ... это умножение, но с таким же успехом может сделать его разделением:

https://github.com/dwelch67/stm32vld/ Тогда STM32F4D/float01/vectors.s

Я не сделал время, хотя, чтобы увидеть, как быстро/медленно. Понятно, что я использую кору-м выше, и вы говорите о коре-а, разных концах спектра, аналогичных инструкциях по плаванию, и материал GCC LIB похожи Так же легко построить для руки. На самом деле с GCC все должно работать автоматически, вам не нужно делать это так, как я это сделал. Другие компиляторы также не должны делать это так, как я делал это в приключенческой игре выше.

Другие советы

Разделение на постоянное значение выполняется быстро, выполняя 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, для которого <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)

Я написал свою собственную процедуру для выполнения неподписанного разделения, поскольку не смог найти неподписанную версию в Интернете.Мне нужно было разделить 64-битное значение на 32-битное, чтобы получить 32-битный результат.

Внутренний цикл не так эффективен, как решение со знаком, представленное выше, но оно поддерживает арифметику без знака.Эта процедура выполняет 32-разрядное деление, если старшая часть числителя (hi) меньше знаменателя (den), в противном случае выполняется полное 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 Поддержка машины, просто выровняйте первые несколько линий до метки «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 Для подписанного целочисленного подразделения. Они оба ожидают 64-раз r1 (Высокое слово) и r0 (низкое слово) и 32-битный делитель в r2. Анкет Они возвращают коэффициент в r0 и остаток в r1, таким образом, вы можете определить их в C header в качестве extern Вернув 64-разрядное целое число и замаскируйте коэффициент и остаток после этого. Ошибка (разделение на 0 или переполнение) обозначена оставшейся, имеющей абсолютное значение, превышающее или равное абсолютное значение делителя. Алгоритм подписанного разделения использует различие для случая по признакам как дивидендов, так и дивизора; Сначала он не превращается в положительные целые числа, поскольку это не обнаружит все условия переполнения должным образом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top