Reputation: 308
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?
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
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
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
Reputation: 1647
If you have a memory variable called hwcntrlwd with the bits correctly set: (intel syntax)
mov eax,[hwcntrlwd]
fstcw ax
Upvotes: -1
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