Реализация функции возведения в степень - 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
Объяснение кода листинга программы
- Устанавливается режим использования 64-битного кода.
- Объявляются две функции: SIntPower (возведение в степень для знаковых чисел) и UIntPower (возведение в степень для беззнаковых чисел).
- В функции SIntPower проверяется, что степень (Y) не равна 0. Если равна, результат будет 1.
- Если степень (Y) отрицательная, проводятся дополнительные проверки.
- В цикле происходит возведение числа (X) в степень (Y) путем последовательного умножения.
- Если произошло переполнение, CF устанавливается в 1, а результат RAX -1.
- Если степень (Y) отрицательная и число (X) равно 1, результат будет 1.
- Если степень (Y) отрицательная и число (X) равно -1, результат будет 0 или -1 в зависимости от четности степени.
- Функция UIntPower аналогична функции SIntPower, только работает с беззнаковыми числами.
- Результат возвращается в RAX, а флаг CF устанавливается в 1, если произошло переполнение.
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д