Реализация функции возведения в степень - Assembler

Узнай цену своей работы

Формулировка задачи:

Всем привет. Прохожу один курс по асму, попалась такая задача: написать функцию, принимающую два беззнаковых числа (каждое из которых может равняться нулю), возвести в степень и вернуть ответ в rax. Написал такой код:
;числа в rdi и rsi 
pow:
 
    cmpq $0,%rdi
    movq $0,%rax
    jz exit
    cmpq $0,%rsi
    movq $1,%rax
    jz exit
    dec %rsi
    pushq %rdi
    movq %rdi,%rax
more:
    movq (%rsp),%rdx
    mulq %rdx
    dec %rsi
    cmpq $0,%rsi
    ja more
exit:
    retq
Проверяю под отладчиком - возвращает правильный результат, но ответ системой не принимается (значит где-то ошибся). Помогите с поиском ошибки.

Решение задачи: «Реализация функции возведения в степень»

textual
Листинг программы
use64
 
; Входные данные: RCX - база (X), RDX - степень (Y); числа целые знаковые
; Результат: CF=0 - всё хорошо, RAX = X^Y (знаковое число); CF=1 - произошло переполнение, RAX = -1
SIntPower:
                ; Проверка исходных значений
                mov     eax,1                   ; результат RAX = 1 (пока что)
                test    rdx,rdx
;               jz      .exit                   ; если Y = 0, результат = 1 (даже для X = 0) - ЗАКОММЕНТИРОВАНО, т.к. нет смысла, и так будет выход на первом цикле вычислений
                js      .neg                    ; если Y < 0, прыгаем
 
                ; Вычисление
        .loop:  shr     rdx,1                   ; CF = младший бит EDX
                jnc     .nomul                  ; если CF=0, пропускаем умножение
                imul    rax,rcx                 ; иначе RAX = RAX * RCX
                jo      .overflow               ; произошло переполнение?
                test    rdx,rdx
        .nomul: jz      .exit                   ; выходим, если RDX = 0
                imul    rcx,rcx                 ; иначе RCX = RCX^2
                jno     .loop                   ; повторяем цикл, если не было переполнения
        .overflow:
                xor     eax,eax                 ; иначе возвращаем RAX = -1 и CF=1 (это будет после dec rax) в случае переполнения
        .zero:
                dec     rax                     ; результат RAX = 0
        .exit:  ret
 
        .neg:   ; Отрицательная степень
                cmp     rcx,1
                je      .exit                   ; если X = 1, результат = 1 - эти 2 строки можно вынести к началу (после mov eax,1), это ускорит возведение 1 в степень, но замедляют другие случаи возведения в положительную степень
                cmp     rcx,-1
                jne     .zero                   ; если X <> -1, результат будет = 0 (максимально возможное значение: 2^-1 = 0.5, но мы его всё равно округлим в меньшую сторону до 0)
                test    dl,al                   ; иначе (если X = -1), проверяем чётность Y (AL = 1)
                jz      .exit                   ; если Y чётная, результат = 1
                neg     rax                     ; иначе результат = -1 (можно заменить на mov rax,rcx)
                ret
 
; Входные данные: RCX - база (X), RDX - степень (Y); числа целые БЕЗзнаковые
; Результат: CF=0 - всё хорошо, RAX = X^Y (БЕЗзнаковое число); CF=1 - произошло переполнение, RAX = 0FFFFFFFFFFFFFFFFh (-1)
UIntPower:
                ; Подготовка
                mov     eax,1                   ; результат RAX = 1 (пока что)
;               cmp     rcx,rax
;               je      .exit                   ; если X = 1, результат = 1 - ЗАКОММЕНТИРОВАНО, т.к. эти 2 строки ускоряют возведение 1 в степень, но замедляют другие случаи
                mov     r8,rdx                  ; R8 = Y (т.е. RDX будет уничтожен после умножения через mul)
                mov     r9d,0FFFFFFFFh          ; R9 = 0FFFFFFFFh
 
                ; Вычисление
                jmp     .start                  ; пропускаем imul (он нужен только для следующих циклов)
        .loop:  imul    rcx,rcx                 ; RCX = RCX^2 (перемещено сюда, чтобы не делать лишних jcc-инструкций внутри цикла)
        .start: shr     r8,1                    ; CF = младший бит R8
                jnc     .nomul                  ; если CF=0, пропускаем умножение
                mul     rcx                     ; иначе RDX:RAX = RAX * RCX
                test    rdx,rdx
                jnz     .overflow               ; произошло переполнение?
                test    r8,r8
        .nomul: jz      .exit                   ; выходим, если R8 = 0
                cmp     rcx,r9
                jbe     .loop                   ; повторяем цикл, если RCX <= 0FFFFFFFFh (R9), иначе будет переполнение при возведении в квадрат
        .overflow:
                or      rax,-1                  ; иначе возвращаем RAX = 0FFFFFFFFFFFFFFFFh
                stc                             ; и CF=1
        .exit:  ret

Объяснение кода листинга программы

  1. Устанавливается режим использования 64-битного кода.
  2. Объявляются две функции: SIntPower (возведение в степень для знаковых чисел) и UIntPower (возведение в степень для беззнаковых чисел).
  3. В функции SIntPower проверяется, что степень (Y) не равна 0. Если равна, результат будет 1.
  4. Если степень (Y) отрицательная, проводятся дополнительные проверки.
  5. В цикле происходит возведение числа (X) в степень (Y) путем последовательного умножения.
  6. Если произошло переполнение, CF устанавливается в 1, а результат RAX -1.
  7. Если степень (Y) отрицательная и число (X) равно 1, результат будет 1.
  8. Если степень (Y) отрицательная и число (X) равно -1, результат будет 0 или -1 в зависимости от четности степени.
  9. Функция UIntPower аналогична функции SIntPower, только работает с беззнаковыми числами.
  10. Результат возвращается в RAX, а флаг CF устанавливается в 1, если произошло переполнение.

Оцени полезность:

10   голосов , оценка 4.1 из 5