Steller
Steller

Reputation: 308

Rounding to an integer - Rounding control field?

I am trying to write a function named roundD that rounds its first argument to an integer value according to the mode specified by its second argument.

I will be writing the function in assembly language, using gcc’s inline assembler. I do not want to use any predefined functions..

I think i need to set the Rounding Control field of the FPU Control Word in accordance with the second argument?. I also think i need to restore the original value of the Rounding Control field before roundD returns?

I am trying to figure out how i should accomplish this in the right order..

Should i use the Control Word of the FPU?

  1. Do i load the value into the FPU?
  2. Use the control word - BIT FIELDS to do the calculations?

like: The RC field (bits 11 and 10) or Rounding Control determines how the FPU will round results.

Can someone give me an example of how i would use the RC fields? Or am i way off base with what i need to do?

I am going off this page.. http://www.website.masmforum.com/tutorials/fptute/fpuchap1.htm

is this a good page to go from? What else do i need to google for information? Any help is much appreciated.. I think i just need a detailed analysis of the steps i need to take to accomplish this..

This is what i have so far..

#include <stdio.h>
#include <stdlib.h>

#define PRECISION           3
#define RND_CTL_BIT_SHIFT   10

// floating point rounding modes: IA-32 Manual, Vol. 1, p. 4-20
typedef enum {
ROUND_NEAREST_EVEN =    0 << RND_CTL_BIT_SHIFT,
ROUND_MINUS_INF =       1 << RND_CTL_BIT_SHIFT,
ROUND_PLUS_INF =        2 << RND_CTL_BIT_SHIFT,
ROUND_TOWARD_ZERO =     3 << RND_CTL_BIT_SHIFT
} RoundingMode;

double roundD (double n, RoundingMode roundingMode)
{

return n;


}

int main (int argc, char **argv)
{
double  n = 0.0;

printf("Rounding - Assembly");
if (argc > 1)
    n = atof(argv[1]);

printf("roundD even %.*f = %.*f\n",
       PRECISION, n, PRECISION, roundD(n, ROUND_NEAREST_EVEN));
printf("roundD down %.*f = %.*f\n",
       PRECISION, n, PRECISION, roundD(n, ROUND_MINUS_INF));
printf("roundD up   %.*f = %.*f\n",
       PRECISION, n, PRECISION, roundD(n, ROUND_PLUS_INF));
printf("roundD zero %.*f = %.*f\n",
       PRECISION, n, PRECISION, roundD(n, ROUND_TOWARD_ZERO));

return 0;
}

Upvotes: 1

Views: 1414

Answers (4)

Bri4n
Bri4n

Reputation: 631

 oldcw   dw   ?

  fstcw oldcw     ;get the current Control Word to retain all setting bits
                  ;not related to the rounding control (RC) bits
  fwait       ;to insure the storage instruction is completed
  mov   ax,oldcw
; and   ax,0F3FFh ;clears only the RC bits, leaving all other bits unchanged
                  ;not necessary here because both bits will be set
  or    ax,0C00h  ;this will set both bits of the RC field to the truncating mode
                  ;without affecting any of the other field's bits
  push  eax       ;use the stack to store the modified Control Word in memory
  fldcw [esp]     ;load the modified Control Word

  fxxxx           ;other FPU instruction(s) needing the truncating mode

  fldcw oldcw     ;restore the previous Control Word
  pop   eax       ;clean-up the stack
                  ;this could also retrieve a 16-bit or 32-bit integer
                  ;possibly returned by the "other FPU instruction(s)"

Upvotes: 0

OldBody
OldBody

Reputation: 11

 __ftol          proc near
 var_C           = qword ptr -0Ch
 var_4           = word ptr -4
 var_2           = word ptr -2
                 push    ebp
                 mov     ebp, esp
                 add     esp, 0FFFFFFF4h
                 fstcw   [ebp+var_2]
                 wait
                 mov     ax, [ebp+var_2]
                 or      ah, 0Ch         ; 00001100
                                         ;
                                         ; ROUNDING CONTROL FIELD
                                         ;     The rounding control (RC) field of the FPU
                                         ;     control register (bits 10 and 11)
                                         ;
                                         ;     Rounding Mode                  RC Field Setting
                                         ;
                                         ;     Round nearest     (even)       00b
                                         ;     Round down        (toward -)   01b
                                         ;     Round up          (toward +)   10b
                                         ;     Round toward zero (Truncate)   11b
                 mov     [ebp+var_4], ax
                 fldcw   [ebp+var_4]
                 fistp   [ebp+var_C]
                 fldcw   [ebp+var_2]
                 mov     eax, dword ptr [ebp+var_C]
                 mov     edx, dword ptr [ebp+var_C+4]
                 leave
                 retn
 __ftol          endp

Upvotes: 1

Arthur Kalliokoski
Arthur Kalliokoski

Reputation: 1647

If you have a memory variable called hwcntrlwd with the bits correctly set: (intel syntax)

mov eax,[hwcntrlwd]
fstcw ax

Upvotes: -1

caf
caf

Reputation: 239051

Have you checked if your environment includes the improved C99 floating point support? C99 defines the functions fegetround() and fesetround() to get and set the current floating point rounding mode. The rint() function can then be used to round according to the current rounding mode.

Upvotes: 1

Related Questions