Сборка x86 (NASM) Как получить n-й корень или повысить значение с плавающей запятой с дробью

У меня есть предстоящий проект на следующей неделе, и я провел последние 2 дня, выясняя, как сделать n-й корень.

Мне нужно смоделировать среднее геометрическое в сборке NASM.

Я знаю, что в FPU есть инструкция по извлечению квадратного корня, но на этом я далеко не уйду.

В настоящее время я могу ввести столько входных данных, сколько захочу, и он умножает их без проблем. Но я застрял с частью «nth root», так как не знаю, как это сделать с NASM.

Это мой текущий код:

    global _main
    extern _printf, _scanf, _system

    section .text

    _main:
    mov ebp, esp; for correct debugging
push clr
call _system
add esp, 4

    ; start of INITIALIZE
    ;printf("Enter number of elements")
push print1
call _printf
add esp, 4
    ; scanf number of elements
push n
push scan1
call _scanf
add esp, 8

   ; printf("enter n integers")
   push dword [n]
push print2
call _printf
add esp, 8

   fld qword [n2]


   ; scan first element
   push n2
   push scan2
   call _scanf
   add esp, 8

   fld qword [n2]

   JE J1

   MOV EBX, 1

   CMP EBX, [n]
   JE J1

   L1:

   push n2
   push scan2
   call _scanf 
   add esp, 8

   fld qword [n2]
   fmul st1, st0
   fstp qword [ans]

   INC EBX
   CMP EBX, dword [n]
   JE J1
   LOOP L1

   J1:

   fst qword [ans]

   push dword [ans+4]
   push dword [ans]
   push print3
   call _printf
  add  esp, 12

   mov dword [data8], n

   fld qword [data7]
   fld qword [data8]

   fdiv st1, st0

   fstp qword [ans]


   fst qword [ans]

   push dword [ans+4]
   push dword [ans]
   push print3
   call _printf
  add  esp, 12

   xor eax, eax
   ret

   section .data
   clr      db "cls", 0
   print1   db "Enter the number of elements:", 13,10,0
   scan1    db "%d" , 0
   scan2    db "%lf" , 0
   n        dd 0
   n2       dq 0.0
   n3       dq 0.0
   print2   db "Enter %d values:", 13,10,0
   print3   db "Value is %g", 13, 10,0
   data8    dq 3.0
   ans      dq 0.0
   loopstart dq 1.0
   data7    dq 1.0

Или также возможно использовать внешнюю функцию C для извлечения n-го корня?

РЕДАКТИРОВАТЬ: Пытаюсь использовать _pow из C. У меня есть этот код, но он не работает, может кто-нибудь помочь?

       LEA ESI, [return]

       push ESI
       push dword [pushingthing+4]
       push dword [pushingthing]
       push dword [ans+4]
       push dword [ans]
       push pow1
       call _pow
       add  esp, 24

       section   .data

       return   dq 0.0
       pushingthing dq 2.0
       pow1     dd "%g, %g", 13, 10, 0

Делается это потому, что pow (в C) имеет формат pow(x,y), которые оба являются двойными и возвращают двойное значение. Пожалуйста, проверьте.


person Tix Issence    schedule 02.04.2016    source источник
comment
Конечно, вы можете использовать функцию C pow, зная, что n-й корень равен степени 1/n. В ассемблере можно использовать логарифмы.   -  person Jester    schedule 02.04.2016
comment
Я до сих пор не совсем понимаю, как передавать значения с плавающей запятой в функцию C, которая имеет возвращаемое значение. Возможно ли, чтобы вы меня научили? Я искал в Интернете решение, но я в тупике.   -  person Tix Issence    schedule 02.04.2016
comment
Это описано в соглашении о вызовах для вашей платформы (которое вы не упомянули). Обычно вы передаете входные аргументы в стек процессора и возвращаете результат в st(0).   -  person Jester    schedule 02.04.2016
comment
Я думаю, что соглашение о вызовах - ISAx87. Я все еще не совсем понимаю, поэтому попробую другое решение. Вы упомянули логарифмы в ассемблере, но текущий набор инструкций, который у меня есть, автоматически устанавливается на log2. Я не гений математики, поэтому я действительно понятия не имею, как решить эту проблему.   -  person Tix Issence    schedule 02.04.2016
comment
Обновлен основной пост, чтобы уточнить помощь в использовании функции pow. Пожалуйста помоги. С текущей формулой мне все равно пришлось бы использовать pow, поскольку нет прямого способа поднять 2 по формуле log2 в сборке.   -  person Tix Issence    schedule 02.04.2016


Ответы (1)


Использование push для передачи аргументов, особенно двойных, заставит вашу голову болеть со всеми реверсами и делением пополам :) Вы должны просто выделить место и использовать соответствующие инструкции mov. Вы все еще не сказали, в какой среде вы находитесь, но большинство из них возвращают результат в виде st(0) (т.е. стек с плавающей запятой). Таким образом, примером может быть:

sub esp, 16          ; space for 2 doubles
fld qword [x]        ; load 1st arg
fstp qword [esp]     ; write to stack
fld qword [y]        ; load 2nd arg
fstp qword [esp+8]   ; write to stack
call _pow
; result is now in st(0) print it
mov dword [esp], fmt ; format string
fstp qword [esp+4]   ; pass result as arg
call _printf
add esp, 16          ; free stack space

x: dq 16.0
y: dq 0.25
fmt: db "%g", 13, 10, 0
person Jester    schedule 02.04.2016
comment
Святая мольба, это работает. Спасибо, и ты определенно спасаешь мою шкуру прямо сейчас. - person Tix Issence; 02.04.2016