简体   繁体   English

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

[英]How to divide floating-point number in x86 assembly?

When i try to write Heron algorithm to count sqrt from ECX register, it doesn't work.当我尝试编写 Heron 算法来计算 ECX 寄存器中的 sqrt 时,它不起作用。 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

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):这是一个简短的示例片段(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:编辑:
As harold pointed out, there's also an instruction which computes directly the square root, it is fsqrt .正如 harold 指出的那样,还有一条直接计算平方根的指令,它是fsqrt Both the operand and the result are st0 .操作数和结果都是st0

EDIT #2:编辑#2:
I wasn't sure if you really could load into st0 an immediate value as my reference doesn't specify if clearly.我不确定您是否真的可以将即时值加载到st0 ,因为我的参考没有明确指定。 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 :这些是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:编辑#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 .您缺少fldfst的操作数。 Looking at your comments I suppose you wanted fld [esp] and fst [esp] , I don't get why you're talking about ebp though.看看你的评论,我想你想要fld [esp]fst [esp] ,但我不明白你为什么在谈论ebp 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. ebp应该保存堆栈帧的开头(那里有很多我们不应该弄乱的东西),而esp保存它的结尾。 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.在计算并保存平方根后add esp, 4您还应该在末尾add esp, 4 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.这是因为push ecx也会sub esp, 4在引擎盖下为您推送的值腾出空间,并且在将值保存回来时仍然需要一些空间。 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 .正是为此,您还可以避免sub esp, 100add esp, 100 ,因为房间已经通过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.您建议的代码使用fldfst ,它们都对浮点值进行运算,因此您得到的结果不会是您期望的结果。 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). 00 00 00 A9 是 169 上的字节表示,但它表示浮点数 +2.3681944047089408e-0043(对于那些挑剔的人来说,它实际上是一个长双)。
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

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

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.第一个dword ptr可能是可选的,具体取决于您的汇编程序。

After this code, the result is on the top of the FPU stack, ST(0).在此代码之后,结果位于 FPU 堆栈的顶部,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:我不知道之后你想用它做什么..如果你想把它四舍五入到一个 int 并将它放回 ecx,我建议这样做:

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:我会以 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)

It's a bit easier that way.这样容易一些。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM