x86汇编中如何除浮点数?

发布于 2024-12-25 23:24:00 字数 596 浏览 1 评论 0原文

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

我的算法:

<前><代码> 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

When i try to write Heron algorithm to count sqrt from ECX register, it doesn't work. It looks like the problem is dividing floating numbers, because the result is integer.

My algorithm:

 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

孤檠 2025-01-01 23:24:00

您需要使用浮点指令集来实现您的目标。您可能会发现有用的一些说明是:

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 st1 by st0, then pop from reg stack (again, push the 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)
}

编辑:
正如 harold 指出的,还有一个直接计算平方根的指令,它是 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

您缺少 fldfst 的操作数。看看你的评论,我想你想要 fld [esp]fst [esp],我不明白你为什么在谈论 ebp > 不过。 ebp 应该保存堆栈帧的开头(其中有很多我们不应该搞乱的东西),而 esp 保存它的结尾。我们基本上希望在堆栈帧的末尾进行操作,因为在它之后只有垃圾没人关心。
在计算并保存平方根后,您还应该在末尾添加 esp, 4。这是因为 push ecx 也在底层执行 sub esp, 4 为您推送的值腾出空间,并且在保存值时您仍然需要一些空间。正因为如此,您还可以避免 sub esp, 100add esp, 100,因为房间已经通过 push 为您准备好了.
最后一个“警告”:整数和浮点值以非常不同的方式表示,因此当您知道必须使用这两种类型时,请小心您选择的指令。您建议的代码使用 fldfst,它们都对浮点值进行操作,因此您得到的结果不会是您期望的结果。举个例子? 00 00 00 A9 是 169 的字节表示,但它表示浮点数 +2.3681944047089408e-0043 (对于挑剔的人来说,它实际上是一个 long double)。
所以,最终的代码是:

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

You need to use the Floating Point Instruction Set to achieve your goal. Some instructions you might find useful are:

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 st1 by st0, then pop from reg stack (again, push the result in st0)

Here's a short example snippet (VS2010 inline assembly):

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:
As harold pointed out, there's also an instruction which computes directly the square root, it is fsqrt. Both the operand and the result are st0.

EDIT #2:
I wasn't sure if you really could load into st0 an immediate value as my reference doesn't specify if clearly. Therefore I did a small snippet to check and the result is:

    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] 

These are the bytes at 357A8h:

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

So I have to conclude that, unfortunately, you have to store your numbers somewhere in the main memory both when loading and storing them. Of course using the stack as I suggested above isn't mandatory, in fact you could also have some variables defined in your data segment or somewhere else.

EDIT #3:
Don't worry, assembly is a strong beast to beat ;) Regarding your code:

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

You're missing the operands of fld and fst. Looking at your comments I suppose you wanted fld [esp] and fst [esp], I don't get why you're talking about ebp though. ebp is supposed to hold the beginning of the stack frame (where there's a lot of stuff which we shouldn't mess up with) whereas esp holds the end of it. We basically want to operate at the end of the stack frame because after it there's just junk no one cares about.
You should also add esp, 4 at the end, after you computed and saved the square root. This because push ecx does also sub esp, 4 under the hood to make room for the value you push and you still need some room when saving the value back. It's just for this that you can also avoid sub esp, 100 and add esp, 100, because the room is already made for you by push.
One last "warning": integers and floating point values are represented in very different ways, so when you know you have to use both types be careful about the instructions you choose. The code you suggested uses fld and fst, which both operate on floating point values, so the result you get won't be what you expect it to be. An example? 00 00 00 A9 is the byte representation on 169, but it represents the floating point number +2.3681944047089408e-0043 (for the fussy people out there it is actually a long double).
So, the final code is:

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
岁月流歌 2025-01-01 23:24:00

DIV 用于整数除法 - 您需要 FDIV 进行浮点数(或者在这种特殊情况下更可能是 FIDIV ,因为看起来您正在开始具有整数值)。

DIV is for integer division - you need FDIV for floating point (or more likely FIDIV in this particular case, since it looks like you are starting with an integer value).

梦里泪两行 2025-01-01 23:24:00

我不完全确定您实际想要做什么,所以现在我假设您想要取整数的浮点平方根。

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)。我不知道之后你想用它做什么..如果你想将它舍入为 int 并将其放回 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)

这是一个这样容易一些。

I'm not completely sure what you actually want to do, so for now I'll assume that you want to take the floating-point square root of an integer.

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

The first dword ptr may be optional, depending on your assembler.

After this code, the result is on the top of the FPU stack, ST(0). I don't know what you want to do with it afterwards.. if you want to round it to an int and put it back in ecx, I would suggest this:

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

I'll throw in the SSE2 way for good measure:

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)

It's a bit easier that way.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文