Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Help me get \"y = ((x & 0xf3ff) | (roundingMode))\" into assembly code please! d

ID: 3670003 • Letter: H

Question

Help me get  "y = ((x & 0xf3ff) | (roundingMode))" into assembly code please!

double roundD(double n, RoundingMode roundingMode)
{
RoundingMode x, y;

__asm__(
   "fstcw %w0 "   //stores the FPU control word into a 2-byte memory area
///////////////////////////////////////////

"y = ((x & 0xf3ff) | (roundingMode))"
//////////////////////////////////////////   

"fldcw %w0"       //loads value out of memory and stores it into the FPU control word
   "frndint "        //rounds the contents of ST0 to an integer and stores the result back in ST0.
    "fldcw %w0 "         // load value and store it into FPU control word
    : "=m" (x): "m" (y): "=&t" (n): "m" (x));
  
return n;
}

Explanation / Answer

#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)
{
// do not change anything above this comment

    // Control Word storage; CW is mem16
    char *cw2   = malloc(4*sizeof(char));
    short *newCW = malloc(sizeof(short));
    short *oldCW = malloc(sizeof(short));
    // n = 2.500; // for testing purposes

    // I suspect at least some of this is redundant, but it works
    // If you'd like to critique it, feel free
    asm("fstcw %5;" // store control word in oldCW
        "mov %5, %4;" // copy control word into cw2
        "or %2, %4;" // put new mode into rounding control bits
        "mov %4, %3;" // copy cw2 into newCW
        "fldcw %3;" // Loads newCW into Control
        "fldl %0;" // load n into st(0)
        "frndint;" // round n
        "fstpl %0;" // load st(0) back into n
        "fldcw %5;" // load the old control word from cw
        : "=m" (n)
        : "m" (n), "m" (roundingMode),
        "m" (newCW), "r" (cw2), "m" (oldCW) // mov requires one argument in a register
      );

    // Cleanup, aisle7
    oldCW = NULL;
    free (oldCW);

    newCW = NULL;
    free (newCW);

    cw2 = NULL;
    free (cw2);
    return n;
  
}

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

    if (argc > 1)
        n = atof(argv[1]);

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

    return 0;
}

output


roundD even 0.000 = 0.000                                                                                                                                   
roundD down 0.000 = 0.000                                                                                                                                   
roundD up   0.000 = 0.000                                                                                                                                   
roundD zero 0.000 = 0.000