Dacto
Dacto

Reputation: 2911

Floating point in x86 Assembly

I'm currently trying to get an average as a floating point number, rounded to the nearest .001

Being pretty new to floating point instructions and such, this is what I have so far from the reading and searching that I've done.

sum: a DWORD holding the value 90 (sum of acc number of items)

acc: a DWORD holding the value 7 (number of items)

res: a DWORD to hold the answer.

In the end, edx has the value 0. Why is this? Additionally, how do I round floating point?

Or...is there a better way to do this?

My asm:

   push dword ptr sum
   fild dword ptr [esp]
   push dword ptr acc
   fild dword ptr [esp]
   add esp,4
   fdiv st(0), st(1)
   sub esp,4
   fstp dword ptr [esp]
   pop eax
   add esp,4
   mov edx, eax

Also, I tried this:

   finit
   fild sum
   fidiv acc
   frndint
   fist res
   mov eax, res

But...this results in: 2147483648 (DWORD range)

Upvotes: 3

Views: 7652

Answers (2)

PhiS
PhiS

Reputation: 4650

I am not sure I understand your question correctly, but at least the 2nd bit of code seems valid. Whether it does what you want is another question. It does this:

   finit
   fild sum        ;//ST(0) = 90
   fidiv acc       ;//ST(0) = ST(0) / acc = 90 / 7 = 12.857..
   frndint         ;//ST(0) = 13
   fist res        ;//RES= ST(0) = 13
   mov eax, res    ;//EAX = RES = 13

In other words, you are calculating round(90/7) = 13.

Now, this is a straightforward division, not an average. You'd obviously calculate an average by summing over N items and then dividing by the number of items.

The second thing is that you are working with integers, not with floating point values: the fIld, fIdiv and fIst instructions work are designed to work with integer, not floating point values. If you store a value from the FPU into memory with fIst rather than fst/fstp this value in memory will be an integer value, not a floating point value. Is it this you want?

To store a floating point value from the FPU to memory pointed to by [eax], you can use

   fstp dword ptr [eax] ;// Store Single-Precision Floating point value (4 bytes)
   fstp qword ptr [eax] ;// Store Double-Precision Floating point value (8 bytes)
   fstp tbyte ptr [eax] ;// Store Long Double-Precision Floating point value (10 bytes)

The Single and Double stores (but not the tbyte ptr) also work with fst instead of fstp.

Upvotes: 1

Necrolis
Necrolis

Reputation: 26181

you seem to have a lot of unneeded stack operations in your code, however, there is a more sinister problem here, storing the fp result in a DWORD will truncate it, so there is no point in round it unless you plan on round to the nearest integer value (which is what your second attempt does).

As such I'll base an example off your second attempt:

PUSH ECX
FILD [ESP]
PUSH EDX
FILD [ESP]
FDIV
FRNDINT
ADD ESP,4
FISTP DWORD PTR [ESP]
POP EAX
RETN

this function assumes the value in ECX, the count in EDX and returns a rounded average in EAX. for here you can just change it to use your globals, or call it as a function using your globals

Upvotes: 1

Related Questions