2012-01-10 3 views
3

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

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

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 
+0

Существует инструкция для квадратных корней, кстати, как для кода FPU, так и для кода SSE. Таким образом, вам даже не нужно это делать. – harold

+0

@harold. Есть ли место для квадратных корней в сборке nasm? У меня его нет в моей CodeTable. Вы можете мне это рассказать? –

+1

FSQRT (D9 FA) для кода FPU, SQRTSS (F3 0F 51/r) для SSE и SQRTSD (F2 0F 51/r) для SSE2 (также есть версии, которые принимают 4 упакованных поплавка или 2 упакованных двойника). Вот более полная ссылка: http://siyobik.info/main/reference/ – harold

ответ

5

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

+0

, когда я редактирую DIV, FiDIV или FDIV, это не работает. Наверное, я ошибаюсь. Я уверен, что он работает в nasm? FFTCount32.S: 188: ошибка: недопустимая комбинация кода операции и операндов FFTCount32.S: 192: ошибка: недопустимая комбинация кода операции и операндов –

8

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

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.

EDIT # 2:
Я не был уверен, что если вы действительно можете загрузить в st0 непосредственное значение, как мой reference не указывает, если ясно. Поэтому я сделал небольшой фрагмент кода, чтобы проверить и результат:

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

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

[email protected]: 
000357A8 00 00    add   byte ptr [eax],al 
000357AA 60     pushad 
000357AB 41     inc   ecx 

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

EDIT # 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 
+0

Теперь я понимаю все. Существуют регистры anodher для целых чисел и числа с плавающей запятой. Но, если у меня есть целое число в ECX, и вы хотите иметь квадратный корень из ECX в st0, не используя стек, что мне делать? Это возможно без использования стека? Я пытался что-н, как это: 'Mov \t ECX, 144 \t \t мов st0, ECX \t fsqrt' Но он не работает :( –

+0

@MieszkoMikulski: видеть мое редактирование :) – BlackBear

+0

Спасибо за ваш следующий ответ. Я начинаю, тогда мой уровень понимания низкий.После того, как я прочитал ваш ответ, я пишу код вроде этого 'mov \t ecx, 169; число с i хочу root \t sub \t esp, 100; i перемещение esp для свободного места \t push \t ecx; i сохранить значение ecx \t добавить \t esp, 4; push был перемещен мой ebp, тогда я должен вернуться \t fld \t; i загрузить из esp, тогда я должен загрузить ecx \t fsqrt \t; i sqrt it \t fst; я сохраняю его на ebp + 100 \t добавить \t esp, 100; back esp to ebp' По-моему, он должен работать, как я пишу в комментариях (после ';'), но он не делает. –

4

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

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) 

Это немного проще.