How to divide floating-point number in x86 assembly?

19,264

Solution 1

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

Solution 2

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).

Solution 3

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.

Share:
19,264
Mieszko Mikulski
Author by

Mieszko Mikulski

Updated on June 04, 2022

Comments

  • Mieszko Mikulski
    Mieszko Mikulski almost 2 years

    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
    
    • harold
      harold over 12 years
      There's an instruction for square roots too, by the way, both for FPU code and for SSE code. So you don't even really need this..
    • Mieszko Mikulski
      Mieszko Mikulski over 12 years
      @harold, There is an instrution for square roots in nasm assembly ? I don't have it in my CodeTable. Can you tell me it?
    • harold
      harold over 12 years
      FSQRT (D9 FA) for FPU code, SQRTSS (F3 0F 51 /r) for SSE and SQRTSD (F2 0F 51 /r) for SSE2 (there are also versions that take 4 packed floats or 2 packed doubles). Here's a more complete reference: siyobik.info/main/reference
    • Mieszko Mikulski
      Mieszko Mikulski over 12 years
      >'mov ecx, 144 fcmov ecx fsqrt ' If i try this, it also doesnt work
    • harold
      harold over 12 years
      Well no, you can't load an integer into the floating point stack like that, BlackBear's answer has the solution
  • Mieszko Mikulski
    Mieszko Mikulski over 12 years
    when i edit DIV, to FiDIV, or FDIV it doesn't work. Probably i do sth wrong. U sure, that it work in nasm? FFTCount32.S:188: error: invalid combination of opcode and operands FFTCount32.S:192: error: invalid combination of opcode and operands
  • Mieszko Mikulski
    Mieszko Mikulski over 12 years
    Now i understend all. There are anodher registers for integers, and float numbers. But, if i have integer number in ECX, and wanna have square root from ECX in st0, without using stack, what should i do? It is possible without using stack? I tried sth like this: 'mov ecx, 144 mov st0,ecx fsqrt' But it doesnt work:(
  • Mieszko Mikulski
    Mieszko Mikulski over 12 years
    Thank you for your next answer. I am beginer then my level of understand is low. After i read your answer i write code like this 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 In my opinion it should work like i post in comments (after ';') but it doesnt..
  • harold
    harold over 12 years
    There is a small problem with the code in the third edit, the result is left on the FPU stack.
  • BlackBear
    BlackBear over 12 years
    @harold: you're right, I'll fix. Is this dangerous by the way?
  • harold
    harold over 12 years
    Somewhat.. the OP is a novice, so he could forget to empty the stack, then later functions could fail "out of nowhere". Most compiler generated code won't empty the stack before using it because typical ABI's specify that after a function it must be empty or contain a single item as return value.
  • BlackBear
    BlackBear over 12 years
    @harold: I get it. With assembly order is important more than ever :)
  • Jerfov2
    Jerfov2 about 8 years
    Wow I love the format of the inline assembly in your first example! I might have to use that sometime...