Как разделить число с плавающей точкой в ​​сборке x86? - PullRequest
3 голосов
/ 10 января 2012

Когда я пытаюсь написать алгоритм Герона для подсчета sqrt из регистра ECX, он не работает. Похоже, что проблема заключается в разделении плавающих чисел, потому что результатом является целое число.

Мой алгоритм:

 sqrtecx:
MOV EDX, 10 ; loop count
MOV EAX, 5 ; x_0 in heron algorythm
MOV DWORD[EBP-100], ECX  ; save INPUT (ecx is input)    
MOV DWORD[EBP-104], EDX  ; save loop count
jmp     loop
MOV     ECX, EAX ; move  OUTPUT to ECX

loop:

MOV DWORD[EBP-104], EDX ; save loop count
xor edx, edx

MOV ECX, EAX
MOV     EAX, DWORD[EBP-100]
DIV ECX
ADD EAX, ECX
XOR EDX, EDX
mov ecx, 2
DIV ecx

MOV EDX, DWORD[EBP-104] ; load loop count
DEC EDX
JNZ loop

Ответы [ 3 ]

8 голосов
/ 10 января 2012

Вам нужно использовать набор инструкций с плавающей запятой, чтобы достичь своей цели. Вот некоторые инструкции, которые могут оказаться полезными:

fild <int>  - loads and integer into st0 (not an immediate
faddp       - adds st0 to st1, and pop from reg stack (i.e. result in st0)
fdivp       - divides st0 by st1, then pop from reg stack (again, result in st0)

Вот небольшой пример кода (встроенная сборка VS2010):

int main(void)
{
    float res;

    __asm {
        push    dword ptr 5;     // fild needs a memory location, the trick is
        fild    [esp];           // to use the stack as a temp. storage
        fild    [esp];           // now st0 and st1 both contain (float) 5
        add     esp, 4;          // better not screw up the stack
        fadd    st(0), st(0);    // st0 = st0 + st0 = 10
        fdivp   st(1), st(0);    // st0 = st1 / st0 = 5 / 10 = 0.5
        sub     esp, 4;          // again, let's make some room on the stack
        fstp    [esp];           // store the content of st0 into [esp]
        pop     eax;             // get 0.5 off the stack
        mov     res, eax;        // move it into res (main's local var)
        add     esp, 4;          // preserve the stack
    }

    printf("res is %f", res);    // write the result (0.5)
}

EDIT:
Как отметил Гарольд, есть также инструкция, которая вычисляет непосредственно квадратный корень, это fsqrt. И операнд, и результат st0.

РЕДАКТИРОВАНИЕ № 2:
Я не был уверен, что вы действительно можете загрузить в st0 немедленное значение, так как моя ссылка не указывает, ясно ли. Поэтому я сделал небольшой фрагмент для проверки, и результат:

    float res = 5.0 * 3 - 1;
000313BE D9 05 A8 57 03 00    fld         dword ptr [__real@41600000 (357A8h)]  
000313C4 D9 5D F8             fstp        dword ptr [res] 

Это байты в 357A8h:

__real@41600000:
000357A8 00 00                add         byte ptr [eax],al  
000357AA 60                   pushad  
000357AB 41                   inc         ecx  

Итак, я должен сделать вывод, что, к сожалению, вы должны хранить свои номера где-то в основной памяти как при загрузке, так и при их сохранении. Конечно, использование стека, как я предлагал выше, не является обязательным, на самом деле вы можете также определить некоторые переменные в своем сегменте данных или где-то еще.

РЕДАКТИРОВАТЬ # 3:
Не волнуйтесь, сборка - это сильное чудовище;) Что касается вашего кода:

mov     ecx, 169    ; the number with i wanna to root
sub     esp, 100    ; i move esp for free space
push    ecx         ; i save value of ecx
add     esp,4       ; push was move my ebp,then i must come back 
fld                 ; i load from esp, then i should load ecx 
fsqrt               ; i sqrt it
fst                 ; i save it on ebp+100 
add     esp,100     ; back esp to ebp

Вам не хватает операндов fld и fst. Глядя на ваши комментарии, я полагаю, вы хотели fld [esp] и fst [esp], но я не понимаю, почему вы говорите о ebp. ebp должен содержать начало стекового фрейма (где есть много вещей, с которыми мы не должны путаться), тогда как esp содержит его конец. Мы в основном хотим работать в конце стекового фрейма, потому что после него просто мусор, который никому не нужен.
Вы должны также add esp, 4 в конце, после того как вы вычислили и сохранили квадратный корень. Это потому, что push ecx делает также sub esp, 4 под капотом, чтобы освободить место для значения, которое вы нажимаете, и вам все еще нужно немного места для сохранения значения обратно. Только для этого вы также можете избежать sub esp, 100 и add esp, 100, потому что комната уже создана для вас push.
Одно последнее «предупреждение»: целые числа и значения с плавающей запятой представлены по-разному, поэтому, когда вы знаете, что должны использовать оба типа, будьте осторожны с инструкциями, которые вы выбираете. Код, который вы предложили, использует fld и fst, которые оба работают со значениями с плавающей запятой, поэтому полученный вами результат будет не таким, как вы ожидаете. Пример? 00 00 00 A9 - это байтовое представление 169, но оно представляет число с плавающей точкой + 2.3681944047089408e-0043 (для суетливых людей это на самом деле длинный дубль). Итак, окончательный код:

mov     ecx, 169;   // the number which we wanna root
push    ecx;        // save it on the stack
fild    [esp];      // load into st0 
fsqrt;              // find the square root
fistp   [esp];      // save it back on stack (as an integer)
// or fst [esp] for saving it as a float
pop ecx;            // get it back in ecx
5 голосов
/ 10 января 2012

DIV для целочисленного деления - вам нужно FDIV для плавающей запятой (или, скорее всего, FIDIV в данном конкретном случае, поскольку похоже, что вы начинаете с целочисленного значения).

4 голосов
/ 10 января 2012

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

mov dword ptr[esp],ecx   ; can't load a GRP onto the FPU stack, so go through mem
fild dword ptr[esp]      ; read it back (as integer, converted to float)
fsqrt                    ; take the square root

Первый dword ptr может быть необязательным, в зависимости от вашего ассемблера.

После этого кода результат находится на вершине стека FPU, ST (0). Я не знаю, что вы хотите сделать с ним потом ... если вы хотите округлить его до целого числа и вернуть его в ecx, я бы предложил это:

fistp dword ptr[esp]     ; again it can't go directly, it has to go through mem
mov ecx,dword ptr[esp]

Я добавлю SSE2 для хорошей меры:

cvtsi2sd xmm0,ecx  ; convert int to double
sqrtsd xmm0,xmm0   ; take the square root
cvtsd2si ecx,xmm0  ; round back to int (cvttsd2si for truncate instead of round)

Так немного проще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...