如何在x86汇编中划分浮点数?

Mie*_*ski 3 floating-point assembly

当我尝试编写Heron算法来计算来自ECX寄存器的sqrt时,它不起作用.看起来问题是划分浮点数,因为结果是整数.

我的算法:

 sqrtecx:
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)

Bla*_*ear 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)
Run Code Online (Sandbox Code Playgroud)

这是一个简短的示例代码段(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)
}
Run Code Online (Sandbox Code Playgroud)

编辑:
正如哈罗德所指出的那样,还有一条直接计算平方根的指令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] 
Run Code Online (Sandbox Code Playgroud)

这些是以下字节357A8h:

__real@41600000:
000357A8 00 00                add         byte ptr [eax],al  
000357AA 60                   pushad  
000357AB 41                   inc         ecx  
Run Code Online (Sandbox Code Playgroud)

因此,我必须得出结论,遗憾的是,在加载和存储它们时,您必须将数字存储在主存储器中的某个位置.当然,如上所述使用堆栈不是强制性的,实际上您也可以在数据段或其他地方定义一些变量.

编辑#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
Run Code Online (Sandbox Code Playgroud)

你错过了fld和的操作数fst.看着你的意见,我想你想fld [esp]fst [esp]我不知道为什么你在谈论ebp虽然.ebp应该保持堆栈框架的开头(那里有许多我们不应该搞砸的东西),而esp保持它的结束.我们基本上希望在堆栈帧结束时进行操作,因为在它之后只有垃圾无人问津.在计算并保存平方根之后,您
还应该add esp, 4在最后.这push ecx也是因为我们还可sub esp, 4以为您推送的价值腾出空间,在保存价值时仍然需要一些空间.这只是这个,你也可避免sub esp, 100add esp, 100,因为房间已经为你所作push.
最后一个"警告":整数和浮点值以非常不同的方式表示,因此当您知道必须使用这两种类型时,请注意您选择的指令.该代码你建议的使用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
Run Code Online (Sandbox Code Playgroud)


Pau*_*l R 5

DIV用于整数除法 - 您需要FDIV浮点(或者更有可能FIDIV在这种特殊情况下,因为它看起来像是以整数值开头).