| #include <stdio.h> |
| #include <stdlib.h> |
| #include <math.h> |
| |
| #include "soft-fp.h" |
| #include "single.h" |
| #include "double.h" |
| |
| #include <fpu_control.h> |
| |
| /*======================================================================*/ |
| /* declarations for the functions we are testing */ |
| |
| double __adddf3(double, double); |
| double __subdf3(double, double); |
| double __muldf3(double, double); |
| double __divdf3(double, double); |
| double __negdf2(double); |
| double __sqrtdf2(double); |
| double __negdf3(double a, double dummy) { return __negdf2(a); } |
| double __sqrtdf3(double a, double dummy) { return __sqrtdf2(a); } |
| |
| float __addsf3(float, float); |
| float __subsf3(float, float); |
| float __mulsf3(float, float); |
| float __divsf3(float, float); |
| float __negsf2(float); |
| float __sqrtsf2(float); |
| float __negsf3(float a, float dummy) { return __negsf2(a); } |
| float __sqrtsf3(float a, float dummy) { return __sqrtsf2(a); } |
| |
| int __fixdfsi(double); |
| int __fixsfsi(float); |
| double __floatsidf(int); |
| float __floatsisf(int); |
| double __extendsfdf2(float); |
| float __truncdfsf2(double); |
| |
| int __eqdf2(double, double); |
| int __nedf2(double, double); |
| int __gtdf2(double, double); |
| int __gedf2(double, double); |
| int __ltdf2(double, double); |
| int __ledf2(double, double); |
| |
| int __eqsf2(float, float); |
| int __nesf2(float, float); |
| int __gtsf2(float, float); |
| int __gesf2(float, float); |
| int __ltsf2(float, float); |
| int __lesf2(float, float); |
| |
| /*======================================================================*/ |
| /* definitions for functions we are checking against */ |
| |
| double r_adddf3(double a, double b) { return a + b; } |
| double r_subdf3(double a, double b) { return a - b; } |
| double r_muldf3(double a, double b) { return a * b; } |
| double r_divdf3(double a, double b) { return a / b; } |
| double r_negdf3(double a, double b) { return -a; } |
| double sqrt(double x); |
| double r_sqrtdf3(double a, double b) { return sqrt(a); } |
| |
| float r_addsf3(float a, float b) { return a + b; } |
| float r_subsf3(float a, float b) { return a - b; } |
| float r_mulsf3(float a, float b) { return a * b; } |
| float r_divsf3(float a, float b) { return a / b; } |
| float r_negsf3(float a, float b) { return -a; } |
| float sqrtf(float x); |
| float r_sqrtsf3(float a, float b) { return sqrtf(a); } |
| |
| int r_fixdfsi(double a) { return (int)a; } |
| int r_fixsfsi(float a) { return (int)a; } |
| double r_floatsidf(int a) { return (double)a; } |
| float r_floatsisf(int a) { return (float)a; } |
| double r_extendsfdf2(float a) { return (double)a; } |
| float r_truncdfsf2(double a) { return (float)a; } |
| |
| int r_eqdf2(double a, double b) { return !(a == b); } |
| int r_nedf2(double a, double b) { return a != b; } |
| int r_gtdf2(double a, double b) { return a > b; } |
| int r_gedf2(double a, double b) { return (a >= b) - 1; } |
| int r_ltdf2(double a, double b) { return -(a < b); } |
| int r_ledf2(double a, double b) { return 1 - (a <= b); } |
| |
| int r_eqsf2(float a, float b) { return !(a == b); } |
| int r_nesf2(float a, float b) { return a != b; } |
| int r_gtsf2(float a, float b) { return a > b; } |
| int r_gesf2(float a, float b) { return (a >= b) - 1; } |
| int r_ltsf2(float a, float b) { return -(a < b); } |
| int r_lesf2(float a, float b) { return 1 - (a <= b); } |
| |
| /*======================================================================*/ |
| |
| void print_float(float x) |
| { |
| union _FP_UNION_S ux; |
| ux.flt = x; |
| printf("%-20.8e %X %02X %06lX", |
| x, ux.bits.sign, ux.bits.exp, (unsigned long)ux.bits.frac); |
| } |
| |
| void print_double(double x) |
| { |
| union _FP_UNION_D ux; |
| ux.flt = x; |
| #if _FP_W_TYPE_SIZE < _FP_FRACBITS_D |
| printf("%-30.18e %X %04X %06lX%08lX", |
| x, ux.bits.sign, ux.bits.exp, |
| (unsigned long)ux.bits.frac1, (unsigned long)ux.bits.frac0); |
| #else |
| printf("%-30.18e %X %04X %014lX", |
| x, ux.bits.sign, ux.bits.exp, |
| (unsigned long)ux.bits.frac); |
| #endif |
| } |
| |
| float rand_float(void) |
| { |
| union { |
| union _FP_UNION_S u; |
| int i; |
| } u; |
| |
| u.i = lrand48() << 1; |
| |
| if (u.u.bits.exp == _FP_EXPMAX_S) |
| u.u.bits.exp--; |
| else if (u.u.bits.exp == 0 && u.u.bits.frac != 0) |
| u.u.bits.exp++; |
| |
| return u.u.flt; |
| } |
| |
| |
| double rand_double(void) |
| { |
| union { |
| union _FP_UNION_D u; |
| int i[2]; |
| } u; |
| |
| u.i[0] = lrand48() << 1; |
| u.i[1] = lrand48() << 1; |
| |
| if (u.u.bits.exp == _FP_EXPMAX_D) |
| u.u.bits.exp--; |
| #if _FP_W_TYPE_SIZE < _FP_FRACBITS_D |
| else if (u.u.bits.exp == 0 && !(u.u.bits.frac0 == 0 && u.u.bits.frac1 == 0)) |
| u.u.bits.exp++; |
| #else |
| else if (u.u.bits.exp == 0 && u.u.bits.frac != 0) |
| u.u.bits.exp++; |
| #endif |
| |
| return u.u.flt; |
| } |
| |
| #define NSPECIALS 10 |
| |
| float gen_special_float(int i) |
| { |
| FP_DECL_EX; |
| FP_DECL_S(X); |
| float x; |
| |
| switch (i & ~1) |
| { |
| case 0: |
| X_c = FP_CLS_NAN; X_f = 0x1234; |
| break; |
| case 2: |
| X_c = FP_CLS_NAN; X_f = 0x1; |
| break; |
| case 4: |
| X_c = FP_CLS_INF; |
| break; |
| case 6: |
| X_c = FP_CLS_ZERO; |
| break; |
| case 8: |
| X_c = FP_CLS_NORMAL; X_e = 0; |
| X_f = 0x4321; |
| break; |
| } |
| X_s = (i & 1); |
| |
| FP_PACK_S(x, X); |
| return x; |
| } |
| |
| double gen_special_double(int i) |
| { |
| FP_DECL_EX; |
| FP_DECL_D(X); |
| double x; |
| |
| switch (i & ~1) |
| { |
| case 0: |
| X_c = FP_CLS_NAN; |
| #if _FP_W_TYPE_SIZE < _FP_FRACBITS_D |
| __FP_FRAC_SET_2(X, _FP_QNANNEGATEDP ? 0 : _FP_QNANBIT_D, 0x1234); |
| #else |
| _FP_FRAC_SET_1(X, (_FP_QNANNEGATEDP ? 0 : _FP_QNANBIT_D) | 0x1234); |
| #endif |
| break; |
| case 2: |
| X_c = FP_CLS_NAN; |
| #if _FP_W_TYPE_SIZE < _FP_FRACBITS_D |
| __FP_FRAC_SET_2(X, _FP_QNANNEGATEDP ? 0 : _FP_QNANBIT_D, 0x1); |
| #else |
| _FP_FRAC_SET_1(X, (_FP_QNANNEGATEDP ? 0 : _FP_QNANBIT_D) | 0x1); |
| #endif |
| break; |
| case 4: |
| X_c = FP_CLS_INF; |
| break; |
| case 6: |
| X_c = FP_CLS_ZERO; |
| break; |
| case 8: |
| X_c = FP_CLS_NORMAL; X_e = 0; |
| #if _FP_W_TYPE_SIZE < _FP_FRACBITS_D |
| __FP_FRAC_SET_2(X, 0, 0x87654321); |
| #else |
| _FP_FRAC_SET_1(X, 0x87654321); |
| #endif |
| break; |
| } |
| X_s = (i & 1); |
| |
| FP_PACK_D(x, X); |
| return x; |
| } |
| |
| float build_float(const char *s, const char *e, const char *f) |
| { |
| union _FP_UNION_S u; |
| |
| u.bits.sign = strtoul(s, 0, 16); |
| u.bits.exp = strtoul(e, 0, 16); |
| u.bits.frac = strtoul(f, 0, 16); |
| |
| return u.flt; |
| } |
| |
| double build_double(const char *s, const char *e, const char *f) |
| { |
| union _FP_UNION_D u; |
| |
| u.bits.sign = strtoul(s, 0, 16); |
| u.bits.exp = strtoul(e, 0, 16); |
| #if _FP_W_TYPE_SIZE < _FP_FRACBITS_D |
| { |
| size_t len = strlen(f)+1; |
| char *dup = memcpy(alloca(len), f, len); |
| char *low = dup + len - _FP_W_TYPE_SIZE/4 - 1; |
| |
| u.bits.frac0 = strtoul(low, 0, 16); |
| *low = 0; |
| u.bits.frac1 = strtoul(dup, 0, 16); |
| } |
| #else |
| u.bits.frac = strtoul(f, 0, 16); |
| #endif |
| |
| return u.flt; |
| } |
| |
| /*======================================================================*/ |
| |
| fpu_control_t fcw0, fcw1; |
| |
| void test_float_arith(float (*tf)(float, float), |
| float (*rf)(float, float), |
| float x, float y) |
| { |
| float tr, rr; |
| rr = (*rf)(x, y); |
| tr = (*tf)(x, y); |
| if (memcmp(&tr, &rr, sizeof(float)) != 0) |
| { |
| fputs("error:\n\tx = ", stdout); print_float(x); |
| fputs("\n\ty = ", stdout); print_float(y); |
| fputs("\n\ttrue = ", stdout); print_float(rr); |
| fputs("\n\tfalse = ", stdout); print_float(tr); |
| putchar('\n'); |
| } |
| } |
| |
| void test_double_arith(double (*tf)(double, double), |
| double (*rf)(double, double), |
| double x, double y) |
| { |
| double tr, rr; |
| #ifdef __i386__ |
| /* Don't worry. Even this does not make it error free |
| on ia32. If the result is denormal, it will not |
| honour the double precision and generate bad results |
| anyway. On the other side, who wants to use ia32 |
| for IEEE math? I don't. */ |
| _FPU_GETCW(fcw0); |
| fcw1 = ((fcw0 & ~_FPU_EXTENDED) | _FPU_DOUBLE); |
| _FPU_SETCW(fcw1); |
| #endif |
| rr = (*rf)(x, y); |
| #ifdef __i386__ |
| _FPU_SETCW(fcw0); |
| #endif |
| tr = (*tf)(x, y); |
| if (memcmp(&tr, &rr, sizeof(double)) != 0) |
| { |
| fputs("error:\n\tx = ", stdout); print_double(x); |
| fputs("\n\ty = ", stdout); print_double(y); |
| fputs("\n\ttrue = ", stdout); print_double(rr); |
| fputs("\n\tfalse = ", stdout); print_double(tr); |
| putchar('\n'); |
| } |
| } |
| |
| void test_float_double_conv(float x) |
| { |
| double tr, rr; |
| rr = r_extendsfdf2(x); |
| tr = __extendsfdf2(x); |
| if (memcmp(&tr, &rr, sizeof(double)) != 0) |
| { |
| fputs("error:\n\tx = ", stdout); print_float(x); |
| fputs("\n\ttrue = ", stdout); print_double(rr); |
| fputs("\n\tfalse = ", stdout); print_double(tr); |
| putchar('\n'); |
| } |
| } |
| |
| void test_double_float_conv(double x) |
| { |
| float tr, rr; |
| rr = r_truncdfsf2(x); |
| tr = __truncdfsf2(x); |
| if (memcmp(&tr, &rr, sizeof(float)) != 0) |
| { |
| fputs("error:\n\tx = ", stdout); print_double(x); |
| fputs("\n\ttrue = ", stdout); print_float(rr); |
| fputs("\n\tfalse = ", stdout); print_float(tr); |
| putchar('\n'); |
| } |
| } |
| |
| void test_int_float_conv(int x) |
| { |
| float tr, rr; |
| rr = r_floatsisf(x); |
| tr = __floatsisf(x); |
| if (memcmp(&tr, &rr, sizeof(float)) != 0) |
| { |
| printf("error\n\tx = %d", x); |
| fputs("\n\ttrue = ", stdout); print_float(rr); |
| fputs("\n\tfalse = ", stdout); print_float(tr); |
| putchar('\n'); |
| } |
| } |
| |
| void test_int_double_conv(int x) |
| { |
| double tr, rr; |
| rr = r_floatsidf(x); |
| tr = __floatsidf(x); |
| if (memcmp(&tr, &rr, sizeof(double)) != 0) |
| { |
| printf("error\n\tx = %d", x); |
| fputs("\n\ttrue = ", stdout); print_double(rr); |
| fputs("\n\tfalse = ", stdout); print_double(tr); |
| putchar('\n'); |
| } |
| } |
| |
| void test_float_int_conv(float x) |
| { |
| int tr, rr; |
| rr = r_fixsfsi(x); |
| tr = __fixsfsi(x); |
| if (rr != tr) |
| { |
| fputs("error:\n\tx = ", stdout); print_float(x); |
| printf("\n\ttrue = %d\n\tfalse = %d\n", rr, tr); |
| } |
| } |
| |
| void test_double_int_conv(double x) |
| { |
| int tr, rr; |
| rr = r_fixsfsi(x); |
| tr = __fixsfsi(x); |
| if (rr != tr) |
| { |
| fputs("error:\n\tx = ", stdout); print_double(x); |
| printf("\n\ttrue = %d\n\tfalse = %d\n", rr, tr); |
| } |
| } |
| |
| int eq0(int x) { return x == 0; } |
| int ne0(int x) { return x != 0; } |
| int le0(int x) { return x <= 0; } |
| int lt0(int x) { return x < 0; } |
| int ge0(int x) { return x >= 0; } |
| int gt0(int x) { return x > 0; } |
| |
| void test_float_cmp(int (*tf)(float, float), |
| int (*rf)(float, float), |
| int (*cmp0)(int), |
| float x, float y) |
| { |
| int tr, rr; |
| rr = (*rf)(x, y); |
| tr = (*tf)(x, y); |
| if (cmp0(rr) != cmp0(tr)) |
| { |
| fputs("error:\n\tx = ", stdout); print_float(x); |
| fputs("\n\ty = ", stdout); print_float(y); |
| printf("\n\ttrue = %d\n\tfalse = %d\n", rr, tr); |
| } |
| } |
| |
| void test_double_cmp(int (*tf)(double, double), |
| int (*rf)(double, double), |
| int (*cmp0)(int), |
| double x, double y) |
| { |
| int tr, rr; |
| rr = (*rf)(x, y); |
| tr = (*tf)(x, y); |
| if (cmp0(rr) != cmp0(tr)) |
| { |
| fputs("error:\n\tx = ", stdout); print_double(x); |
| fputs("\n\ty = ", stdout); print_double(y); |
| printf("\n\ttrue = %d\n\tfalse = %d\n", rr, tr); |
| } |
| } |
| |
| |
| /*======================================================================*/ |
| |
| |
| int main(int ac, char **av) |
| { |
| #ifdef __alpha__ |
| __ieee_set_fp_control(0); |
| #endif |
| av++, ac--; |
| switch (*(*av)++) |
| { |
| { |
| float (*r)(float, float); |
| float (*t)(float, float); |
| |
| do { |
| case 'a': r = r_addsf3; t = __addsf3; break; |
| case 's': r = r_subsf3; t = __subsf3; break; |
| case 'm': r = r_mulsf3; t = __mulsf3; break; |
| case 'd': r = r_divsf3; t = __divsf3; break; |
| case 'r': r = r_sqrtsf3; t = __sqrtsf3; break; |
| case 'j': r = r_negsf3; t = __negsf3; break; |
| } while (0); |
| |
| switch (*(*av)++) |
| { |
| case 'n': |
| { |
| int count = (ac > 1 ? atoi(av[1]) : 100); |
| while (count--) |
| test_float_arith(t, r, rand_float(), rand_float()); |
| } |
| break; |
| |
| case 's': |
| { |
| int i, j; |
| for (i = 0; i < NSPECIALS; i++) |
| for (j = 0; j < NSPECIALS; j++) |
| test_float_arith(t, r, gen_special_float(i), |
| gen_special_float(j)); |
| } |
| break; |
| |
| case 0: |
| if (ac < 7) abort(); |
| test_float_arith(t, r, build_float(av[1], av[2], av[3]), |
| build_float(av[4], av[5], av[6])); |
| break; |
| } |
| } |
| break; |
| |
| { |
| double (*r)(double, double); |
| double (*t)(double, double); |
| |
| do { |
| case 'A': r = r_adddf3; t = __adddf3; break; |
| case 'S': r = r_subdf3; t = __subdf3; break; |
| case 'M': r = r_muldf3; t = __muldf3; break; |
| case 'D': r = r_divdf3; t = __divdf3; break; |
| case 'R': r = r_sqrtdf3; t = __sqrtdf3; break; |
| case 'J': r = r_negdf3; t = __negdf3; break; |
| } while (0); |
| |
| switch (*(*av)++) |
| { |
| case 'n': |
| { |
| int count = (ac > 1 ? atoi(av[1]) : 100); |
| while (count--) |
| test_double_arith(t, r, rand_double(), rand_double()); |
| } |
| break; |
| |
| case 's': |
| { |
| int i, j; |
| for (i = 0; i < NSPECIALS; i++) |
| for (j = 0; j < NSPECIALS; j++) |
| test_double_arith(t, r, gen_special_double(i), |
| gen_special_double(j)); |
| } |
| break; |
| |
| case 0: |
| if (ac < 7) abort(); |
| test_double_arith(t, r, build_double(av[1], av[2], av[3]), |
| build_double(av[4], av[5], av[6])); |
| break; |
| } |
| } |
| break; |
| |
| case 'c': |
| switch (*(*av)++) |
| { |
| case 'n': |
| { |
| int count = (ac > 1 ? atoi(av[1]) : 100); |
| while (count--) |
| test_float_double_conv(rand_float()); |
| } |
| break; |
| |
| case 's': |
| { |
| int i; |
| for (i = 0; i < NSPECIALS; i++) |
| test_float_double_conv(gen_special_float(i)); |
| } |
| break; |
| |
| case 0: |
| if (ac < 4) abort(); |
| test_float_double_conv(build_float(av[1], av[2], av[3])); |
| break; |
| } |
| break; |
| |
| case 'C': |
| switch (*(*av)++) |
| { |
| case 'n': |
| { |
| int count = (ac > 1 ? atoi(av[1]) : 100); |
| while (count--) |
| test_double_float_conv(rand_double()); |
| } |
| break; |
| |
| case 's': |
| { |
| int i; |
| for (i = 0; i < NSPECIALS; i++) |
| test_double_float_conv(gen_special_double(i)); |
| } |
| break; |
| |
| case 0: |
| if (ac < 4) abort(); |
| test_double_float_conv(build_double(av[1], av[2], av[3])); |
| break; |
| } |
| break; |
| |
| case 'i': |
| switch (*(*av)++) |
| { |
| case 'n': |
| { |
| int count = (ac > 1 ? atoi(av[1]) : 100); |
| while (count--) |
| test_int_float_conv(lrand48() << 1); |
| } |
| break; |
| |
| case 0: |
| if (ac < 2) abort(); |
| test_int_float_conv(strtol(av[1], 0, 0)); |
| break; |
| } |
| break; |
| |
| case 'I': |
| switch (*(*av)++) |
| { |
| case 'n': |
| { |
| int count = (ac > 1 ? atoi(av[1]) : 100); |
| while (count--) |
| test_int_double_conv(lrand48() << 1); |
| } |
| break; |
| |
| case 0: |
| if (ac < 2) abort(); |
| test_int_double_conv(strtol(av[1], 0, 0)); |
| break; |
| } |
| break; |
| |
| case 'f': |
| switch (*(*av)++) |
| { |
| case 'n': |
| { |
| int count = (ac > 1 ? atoi(av[1]) : 100); |
| while (count--) |
| test_float_int_conv(rand_float()); |
| } |
| break; |
| |
| case 's': |
| { |
| int i; |
| for (i = 0; i < NSPECIALS; i++) |
| test_float_int_conv(gen_special_float(i)); |
| } |
| break; |
| |
| case 0: |
| if (ac < 4) abort(); |
| test_float_int_conv(build_float(av[1], av[2], av[3])); |
| break; |
| } |
| break; |
| |
| case 'F': |
| switch (*(*av)++) |
| { |
| case 'n': |
| { |
| int count = (ac > 1 ? atoi(av[1]) : 100); |
| while (count--) |
| test_double_int_conv(rand_double()); |
| } |
| break; |
| |
| case 's': |
| { |
| int i; |
| for (i = 0; i < NSPECIALS; i++) |
| test_double_int_conv(gen_special_double(i)); |
| } |
| break; |
| |
| case 0: |
| if (ac < 4) abort(); |
| test_double_int_conv(build_double(av[1], av[2], av[3])); |
| break; |
| } |
| break; |
| |
| { |
| int (*r)(float, float); |
| int (*t)(float, float); |
| int (*c)(int); |
| |
| do { |
| case 'e': r = r_eqsf2; t = __eqsf2; c = eq0; break; |
| case 'n': r = r_nesf2; t = __nesf2; c = ne0; break; |
| case 'l': |
| switch (*(*av)++) |
| { |
| case 'e': r = r_lesf2; t = __lesf2; c = le0; break; |
| case 't': r = r_ltsf2; t = __ltsf2; c = lt0; break; |
| } |
| break; |
| case 'g': |
| switch (*(*av)++) |
| { |
| case 'e': r = r_gesf2; t = __gesf2; c = ge0; break; |
| case 't': r = r_gtsf2; t = __gtsf2; c = gt0; break; |
| } |
| break; |
| } while (0); |
| |
| switch (*(*av)++) |
| { |
| case 'n': |
| { |
| int count = (ac > 1 ? atoi(av[1]) : 100); |
| while (count--) |
| test_float_cmp(t, r, c, rand_float(), rand_float()); |
| } |
| break; |
| |
| case 's': |
| { |
| int i, j; |
| for (i = 0; i < NSPECIALS; i++) |
| for (j = 0; j < NSPECIALS; j++) |
| test_float_cmp(t, r, c, gen_special_float(i), |
| gen_special_float(j)); |
| } |
| break; |
| |
| case 0: |
| if (ac < 7) abort(); |
| test_float_cmp(t, r, c, build_float(av[1], av[2], av[3]), |
| build_float(av[4], av[5], av[6])); |
| break; |
| } |
| } |
| break; |
| |
| { |
| int (*r)(double, double); |
| int (*t)(double, double); |
| int (*c)(int); |
| |
| do { |
| case 'E': r = r_eqdf2; t = __eqdf2; c = eq0; break; |
| case 'N': r = r_nedf2; t = __nedf2; c = ne0; break; |
| case 'L': |
| switch (*(*av)++) |
| { |
| case 'E': r = r_ledf2; t = __ledf2; c = le0; break; |
| case 'T': r = r_ltdf2; t = __ltdf2; c = lt0; break; |
| } |
| break; |
| case 'G': |
| switch (*(*av)++) |
| { |
| case 'E': r = r_gedf2; t = __gedf2; c = ge0; break; |
| case 'T': r = r_gtdf2; t = __gtdf2; c = gt0; break; |
| } |
| break; |
| } while (0); |
| |
| switch (*(*av)++) |
| { |
| case 'n': |
| { |
| int count = (ac > 1 ? atoi(av[1]) : 100); |
| while (count--) |
| test_double_cmp(t, r, c, rand_double(), rand_double()); |
| } |
| break; |
| |
| case 's': |
| { |
| int i, j; |
| for (i = 0; i < NSPECIALS; i++) |
| for (j = 0; j < NSPECIALS; j++) |
| test_double_cmp(t, r, c, gen_special_double(i), |
| gen_special_double(j)); |
| } |
| break; |
| |
| case 0: |
| if (ac < 7) abort(); |
| test_double_cmp(t, r, c, build_double(av[1], av[2], av[3]), |
| build_double(av[4], av[5], av[6])); |
| break; |
| } |
| } |
| break; |
| |
| default: |
| abort(); |
| } |
| |
| return 0; |
| } |