2012-01-10 2 views
3

ECX 레지스터에서 sqrt를 계산하기 위해 Heron 알고리즘을 작성하려고하면 작동하지 않습니다. 결과는 정수이므로 부동 소수점을 나누는 것이 문제인 것처럼 보입니다.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 assembly에서 제곱근에 대한 지시가 있습니까? CodeTable에이 코드가 없습니다. 그걸 말해 줄 수 있니? –

+1

FPE 코드의 경우 FSQRT (D9 FA), SSE의 경우 SQRTSS (F3 0F 51/r) 및 SSE2의 경우 SQRTSD (F2 0F 51/r)입니다 (4 개의 부동 소수점 또는 2 개의 압축 복식을 사용하는 버전도 있음). 여기에 더 완전한 레퍼런스입니다 : http://siyobik.info/main/reference/ – harold

답변

5

DIV는 정수 나누기위한 것입니다 - 당신은 정수 값으로 시작하는 것 같습니다 있기 때문에 당신이이 특정한 경우에 지점 (또는 가능성 FIDIV 플로팅 FDIV 필요).

+0

내가 DIV를 편집 할 때, FiDIV에, 또는 FDIV가 작동하지 않습니다. 아마 내가 잘못 했어. 확실 해요, 그것이 멍청이에서 작동합니까? 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) 
} 

편집 :
해롤드가 지적한 바와 같이, 계산 명령도있다 직접 평방근은 ​​fsqrt입니다. 피연산자와 결과는 모두 st0입니다.

편집 # 2 : 당신이 정말 명확 경우 reference 지정하지 않는 내로 st0 즉시 값으로로드 할 수있는 경우
나는 확실하지 않았다. 그러므로 나는 확인하기 위해 작은 조각을했고, 그 결과는 다음과 같습니다

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

그래서 내가 불행하게도, 당신이 어딘가에 당신의 번호를 저장해야, 그 결론을 내릴 필요가 :

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의 바이트는 주 메모리에서로드 및 저장하는 경우. 물론 위에서 제안한대로 스택을 사용하는 것은 필수 사항은 아니며 사실 데이터 세그먼트 또는 다른 곳에 정의 된 변수가있을 수도 있습니다.

수정 # 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 

당신은 fldfst의 피연산자를 놓치고있어. 귀하의 의견을 보면 내가 fld [esp]fst [esp]을 원한다고 생각하지만, 왜 당신이 ebp에 대해 이야기하는지 알 수 없습니다. ebp은 스택 프레임의 시작 부분을 잡아 두어야합니다 (많은 부분이 엉망이되어서는 안되는 반면). esp은 그 끝을 가지고 있습니다. 우리는 기본적으로 스택 프레임의 끝에서 작동하기를 원합니다. 그 이후에는 그냥 아무 쓸모가 없기 때문입니다.
제곱근을 계산하고 저장 한 후에는 끝에 add esp, 4도 입력해야합니다. 이는 push ecxsub esp, 4을 푸시 값으로 사용할 수있는 공간으로 만들기 때문에 값을 다시 저장할 때 여전히 약간의 공간이 필요합니다. 객실이 이미 push으로 만들어져 있기 때문에 sub esp, 100add esp, 100을 피할 수 있습니다.
하나의 마지막 "경고": 정수와 부동 소수점 값은 매우 다른 방식으로 표현되므로 두 가지 유형을 사용해야하는 경우 선택할 지침에주의하십시오. 제안한 코드는 모두 부동 소수점 값에서 작동하는 fldfst을 사용하므로 결과가 예상 한 값과 다를 수 있습니다. 예를 들면? 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

이제 모두 understend합니다. 정수 및 부동 소수점에 대한 양수인 레지스터가 있습니다. 하지만, 내가 ECX에서 정수를 가지고 있고 스택을 사용하지 않고 st0에서 ECX의 제곱근을 원한다면 어떻게해야합니까? 스택을 사용하지 않고도 가능합니까? 는 '\t ECX, 144 \t MOV \t ST0, ECX \t fsqrt를 MOV'그러나 그것은 일 :( –

+0

@MieszkoMikulski 나던 : 나는이 같은 STH을 시도 내 편집을 참조 : – BlackBear

+0

당신의 다음 답변을 주셔서 감사합니다. 나는 이해 수준이 낮고 초보 다.당신의 대답을 읽고 난 후에 이런 코드를 작성하십시오 'mov \t ecx, 169; 내가 뿌리 뽑기를 원하는 숫자 \t sub \t esp, 100; 나는 여유 공간을 위해 esp를 움직인다 \t 푸시 \t ecx; 나는 ecx의 가치를 저장한다 \t 추가 \t esp, 4; 밀어 내 ebp 이동, 그때 나는 다시 와야합니다 \t fld \t; esp에서로드 한 다음 ecx를로드해야합니다. \t fsqrt \t; i sqrt 그것 \t fst; 나는 ebp + 100에 저장한다. \t add \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)의 맨 위에 있습니다.

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

내가 SSE2에 던질거야 : 난 당신이 int로 그것을 반올림 다시 ECX에 넣어하려는 경우, 나는이 제안 .. 당신이 나중에 그것으로 뭘 원하는지 모르는 좋은 측정 방법 :

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) 

이렇게 쉬운 방법입니다.

관련 문제