/* Tests out of range handling for FSIN, FCOS, FSINCOS and FPTAN. Be
careful with the inline assembly -- this program is compiled as
both a 32-bit and 64-bit test. */
#include <stdio.h>
#include <string.h>
#include <assert.h>
typedef unsigned short int UShort;
typedef unsigned int UInt;
typedef double Double;
typedef unsigned long long int ULong;
typedef struct { Double arg; Double st0; Double st1; UShort fpusw; } Res;
#define SHIFT_C3 14
#define SHIFT_C2 10
#define SHIFT_C1 9
#define SHIFT_C0 8
#define my_offsetof(type,memb) ((int)(unsigned long int)&((type*)0)->memb)
void do_fsin ( /*OUT*/Res* r, double d )
{
assert(my_offsetof(Res,arg) == 0);
assert(my_offsetof(Res,st0) == 8);
assert(my_offsetof(Res,st1) == 16);
assert(my_offsetof(Res,fpusw) == 24);
memset(r, 0, sizeof(*r));
r->arg = d;
__asm__ __volatile__(
"finit" "\n\t"
"fldpi" "\n\t"
"fldl 0(%0)" "\n\t" // .arg
"fsin" "\n\t"
"fstsw %%ax" "\n\t"
"fstpl 8(%0)" "\n\t" // .st0
"fstpl 16(%0)" "\n\t" // .st1
"movw %%ax, 24(%0)" "\n\t" // .fpusw
"finit" "\n"
: : "r"(r) : "eax","cc","memory"
);
}
void do_fcos ( /*OUT*/Res* r, double d )
{
assert(my_offsetof(Res,arg) == 0);
assert(my_offsetof(Res,st0) == 8);
assert(my_offsetof(Res,st1) == 16);
assert(my_offsetof(Res,fpusw) == 24);
memset(r, 0, sizeof(*r));
r->arg = d;
__asm__ __volatile__(
"finit" "\n\t"
"fldpi" "\n\t"
"fldl 0(%0)" "\n\t" // .arg
"fcos" "\n\t"
"fstsw %%ax" "\n\t"
"fstpl 8(%0)" "\n\t" // .st0
"fstpl 16(%0)" "\n\t" // .st1
"movw %%ax, 24(%0)" "\n\t" // .fpusw
"finit" "\n"
: : "r"(r) : "eax","cc","memory"
);
}
void do_fsincos ( /*OUT*/Res* r, double d )
{
assert(my_offsetof(Res,arg) == 0);
assert(my_offsetof(Res,st0) == 8);
assert(my_offsetof(Res,st1) == 16);
assert(my_offsetof(Res,fpusw) == 24);
memset(r, 0, sizeof(*r));
r->arg = d;
__asm__ __volatile__(
"finit" "\n\t"
"fldpi" "\n\t"
"fldl 0(%0)" "\n\t" // .arg
"fsincos" "\n\t"
"fstsw %%ax" "\n\t"
"fstpl 8(%0)" "\n\t" // .st0
"fstpl 16(%0)" "\n\t" // .st1
"movw %%ax, 24(%0)" "\n\t" // .fpusw
"finit" "\n"
: : "r"(r) : "eax","cc","memory"
);
}
void do_fptan ( /*OUT*/Res* r, double d )
{
assert(my_offsetof(Res,arg) == 0);
assert(my_offsetof(Res,st0) == 8);
assert(my_offsetof(Res,st1) == 16);
assert(my_offsetof(Res,fpusw) == 24);
memset(r, 0, sizeof(*r));
r->arg = d;
__asm__ __volatile__(
"finit" "\n\t"
"fldpi" "\n\t"
"fldl 0(%0)" "\n\t" // .arg
"fptan" "\n\t"
"fstsw %%ax" "\n\t"
"fstpl 8(%0)" "\n\t" // .st0
"fstpl 16(%0)" "\n\t" // .st1
"movw %%ax, 24(%0)" "\n\t" // .fpusw
"finit" "\n"
: : "r"(r) : "eax","cc","memory"
);
}
void try ( char* name, void(*fn)(Res*,double), double d )
{
Res r;
fn(&r, d);
// Mask out all except C2 (range)
r.fpusw &= (1 << SHIFT_C2);
printf("%s %16e --> %16e %16e %04x\n",
name, r.arg, r.st0, r.st1, (UInt)r.fpusw);
}
int main ( void )
{
Double limit = 9223372036854775808.0; // 2^63
char* names[4] = { "fsin ", "fcos ", "fsincos", "fptan " };
void(*fns[4])(Res*,double) = { do_fsin, do_fcos, do_fsincos, do_fptan };
int i;
for (i = 0; i < 4; i++) {
char* name = names[i];
void (*fn)(Res*,double) = fns[i];
try( name, fn, 0.0 );
try( name, fn, 0.123 );
try( name, fn, -0.456 );
try( name, fn, 37.0 );
try( name, fn, -53.0 );
printf("\n");
try( name, fn, limit * 0.900000 );
try( name, fn, limit * 0.999999 );
try( name, fn, limit * 1.000000 );
try( name, fn, limit * 1.000001 );
try( name, fn, limit * 1.100000 );
printf("\n");
try( name, fn, -limit * 0.900000 );
try( name, fn, -limit * 0.999999 );
try( name, fn, -limit * 1.000000 );
try( name, fn, -limit * 1.000001 );
try( name, fn, -limit * 1.100000 );
printf("\n");
}
return 0;
}