/* 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; }