#!/usr/bin/nickle # Use nickle's extended precision floating point implementation # to generate some simple test vectors for long double math functions typedef struct { real(real a) f; string name; } func_f_f_t; typedef struct { real(real a, real b) f; string name; } func_f_ff_t; typedef struct { real(real a, int b) f; string name; } func_f_fi_t; typedef struct { int(real a) f; string name; } func_i_f_t; string[] limited_funcs = { "ceill", "copysignl", "fabsl", "floorl", "fmaxl", "fminl", "frexpl", "hypotl", "ilogbl", "ldexpl", "logbl", "llrintl", "lrintl", "lroundl", "llroundl", "nanl", "nearbyintl", "rintl", "roundl", "scalbnl", "scalblnl", "truncl", "logbl", "sqrtl", }; bool is_full_func(string name) { for (int i = 0; i < dim(limited_funcs); i++) if (limited_funcs[i] == name) return false; return true; } exception infinity(real v); exception nan(); int prec = 512; int out_prec = 192; string toupper(string s) { string o = ""; for (int i = 0; i < String::length(s); i++) { int c = s[i]; if ('a' <= c && c <= 'z') c = c - 'a' + 'A'; o = o + String::new(c); } return o; } string make_prec(string name) { string prec = toupper(name) + "_PREC"; printf("#ifndef %s\n", prec); printf("#define %s DEFAULT_PREC\n", prec); printf("#endif\n"); return prec; } void gen_real_f_f(func_f_f_t f) { real x, y; string vec = sprintf("%s_vec", f.name); printf("\n"); if (is_full_func(f.name)) printf("#ifdef FULL_LONG_DOUBLE\n"); string prec_name = make_prec(f.name); printf("long_double_test_f_f_t %s[] = {\n", vec); for (x = -10; x <= 10; x += .1) { try { string sy; try { try { y = imprecise(f.f(imprecise(x, prec)), out_prec); sy = sprintf("%.-eL", y); } catch divide_by_zero(real x, real y) { if (x == 0) raise invalid_argument(f.name, 0, x); raise infinity(x); } } catch infinity(real v) { sy = "(long double) INFINITY"; if (v < 0) sy = "-" + sy; } catch nan() { sy = "(long double) NAN"; } printf(" { .line = __LINE__, .x = %.-eL, .y = %s },\n", x, sy); } catch invalid_argument(string s, int i, poly x) { } } printf("};\n\n"); printf("int test_%s(void) {\n", f.name); printf(" unsigned int i;\n"); printf(" int result = 0;\n"); printf(" for (i = 0; i < sizeof(%s)/sizeof(%s[0]); i++) {\n", vec, vec); printf(" long double y = %s(%s[i].x);\n", f.name, vec); printf(" result += check_long_double(\"%s\", %s[i].line, %s, %s[i].y, y);\n", f.name, vec, prec_name, vec); printf(" }\n"); printf(" return result;\n"); printf("}\n"); if (is_full_func(f.name)) printf("#endif /* FULL_LONG_DOUBLE */\n"); } real cbrt(real x) { return x**(1/3); } real exp10(real x) { return 10**x; } real exp2(real x) { return 2**x; } real expm1(real x) { x = imprecise(x); int bits = precision(x); int obits = bits; if (0 < x && x < 1) obits -= exponent(x); x = imprecise(x, obits); return imprecise(exp(x) - 1, bits); } real lgamma(real x) { if (x < 0 && x == floor(x)) raise infinity(1); return log(gamma(x)); } real log1p(real x) { return log(1+x); } real logb(real x) { if (x == 0) raise infinity(-1); return exponent(imprecise(x)) - 1; } real pow10(real x) { return 10**x; } real round(x) { if (x < 0) return -round(-x); return floor(x+0.5); } real trunc(x) { if (x < 0) return -trunc(-x); return floor(x); } real acosh(x) { if (x < 1) raise nan(); return log(x + sqrt(x*x-1)); } real asinh(x) { if (x == 0) return 0; real sign = 1; if (x < 0) { sign = -1; x = -x; } return sign * log(x + sqrt(x*x+1)); } real atanh(x) { if (abs(x) > 1) raise nan(); if (abs(x) == 1) raise infinity(x); return 0.5 * log((1 + x) / (1 - x)); } real cosh(x) { return (exp(x) + exp(-x)) / 2; } real sinh(x) { return (exp(x) - exp(-x)) / 2; } real tanh(x) { return sinh(x) / cosh(x); } real tgamma(real x) { if (x == 0) raise infinity(1); if (x < 0 && x == floor(x)) raise nan(); return gamma(x); } real nearbyint(real x) { real y; if (x < 0) y = ceil(x-0.5); else y = floor(x+0.5); if (abs(x-y) == 0.5) { if (y % 2 != 0) { if (y > 0) y--; else y++; } } return y; } real _erf(real x, real off) { x = imprecise(x); int bits = precision(x); int obits = bits + 512; real factor = 2 / sqrt(pi_value(obits)); x = imprecise(x, obits); off = imprecise(off, obits) / factor; real val = x - off; for (int n = 1; ; n++) { int f = 2 * n + 1; real a = ((-1)**n * x**f) / (n! * f); val += a; if (exponent(val) - exponent(a) > obits) break; } return imprecise(val * factor, bits); } real erf(real x) { return _erf(x, 0); } real erfc(real x) { return -_erf(x, 1); } real jn(real x, int n) { x = imprecise(x); int bits = precision(x); int obits = bits + 512; x = imprecise(x, obits); real val = imprecise(0, obits); for (int m = 0; ; m++) { real a = ((-1)**m / (m! * gamma(m + n + 1))) * (x/2)**(2 * m + n); val += a; if (exponent(val) - exponent(a) > obits) break; } return imprecise(val, bits); } real scalbnl(real x, int exp) { return x * (2 ** exp); } real ldexpl(real x, int exp) { return x * (2 ** exp); } real rintl(real x) { return nearbyint(x); } real j0(real x) = jn(x,0); real j1(real x) = jn(x,1); real default_prec = 1e-20; func_f_f_t[] funcs_f_f = { { .f = acosh, .name = "acoshl" }, { .f = acos, .name = "acosl" }, { .f = asinh, .name = "asinhl" }, { .f = asin, .name = "asinl" }, { .f = atanh, .name = "atanhl" }, { .f = atan, .name = "atanl" }, { .f = cbrt, .name = "cbrtl" }, { .f = ceil, .name = "ceill" }, { .f = cosh, .name = "coshl" }, { .f = cos, .name = "cosl" }, { .f = erfc, .name = "erfcl" }, { .f = erf, .name = "erfl" }, { .f = exp10, .name = "exp10l" }, { .f = exp2, .name = "exp2l" }, { .f = exp, .name = "expl" }, { .f = expm1, .name = "expm1l" }, { .f = floor, .name = "floorl" }, # { .f = j0, .name = "j0l" }, # { .f = j1, .name = "j1l" }, # { .f = jn, .name = "jnl" }, { .f = lgamma, .name = "lgammal" }, { .f = log10, .name = "log10l" }, { .f = log1p, .name = "log1pl" }, { .f = log2, .name = "log2l" }, { .f = logb, .name = "logbl" }, { .f = log, .name = "logl" }, { .f = nearbyint, .name = "nearbyintl" }, # { .f = pow10, .name = "pow10l" }, /* an alias for exp10 */ { .f = rintl, .name = "rintl" }, { .f = round, .name = "roundl" }, { .f = sinh, .name = "sinhl" }, { .f = sin, .name = "sinl" }, { .f = sqrt, .name = "sqrtl" }, { .f = tanh, .name = "tanhl" }, { .f = tan, .name = "tanl" }, { .f = tgamma, .name = "tgammal" }, { .f = trunc, .name = "truncl" }, # { .f = y0, .name = "y0l" }, # { .f = y1, .name = "y1l" }, # { .f = yn, .name = "ynl" }, }; void gen_real_f_ff(func_f_ff_t f) { real x0, x1, y; string vec = sprintf("%s_vec", f.name); printf("\n"); if (is_full_func(f.name)) printf("#ifdef FULL_LONG_DOUBLE\n"); string prec_name = make_prec(f.name); printf("long_double_test_f_ff_t %s[] = {\n", vec); for (x0 = -4; x0 <= 4; x0 += .25) { for (x1 = -4; x1 <= 4; x1 += 0.25) { try { string sy; try { try { y = imprecise(f.f(imprecise(x0, prec), imprecise(x1, prec)), out_prec); sy = sprintf("%.-eL", y); } catch divide_by_zero(real x, real y) { if (x == 0) raise invalid_argument(f.name, 0, x); raise infinity(x); } } catch infinity(real v) { sy = "(long double) INFINITY"; if (v < 0) sy = "-" + sy; } catch nan() { sy = "(long double) NAN"; } printf(" { .line = __LINE__, .x0 = %.-eL, .x1 = %.-eL, .y = %s },\n", x0, x1, sy); } catch invalid_argument(string s, int i, poly x) { } } } printf("};\n\n"); printf("int test_%s(void) {\n", f.name); printf(" unsigned int i;\n"); printf(" int result = 0;\n"); printf(" for (i = 0; i < sizeof(%s)/sizeof(%s[0]); i++) {\n", vec, vec); printf(" long double y = %s(%s[i].x0, %s[i].x1);\n", f.name, vec, vec); printf(" result += check_long_double(\"%s\", %s[i].line, %s, %s[i].y, y);\n", f.name, vec,prec_name, vec); printf(" }\n"); printf(" return result;\n"); printf("}\n"); if (is_full_func(f.name)) printf("#endif /* FULL_LONG_DOUBLE */\n"); } real fmod(real x, real y) { if (y == 0) raise nan(); real n = x / y; if (n < 0) n = ceil(n); else n = floor(n); return x - n * y; } real fdim(real x, real y) { return max(x-y, 0); } real fmax(real x, real y) { return max(x,y); } real fmin(real x, real y) { return min(x,y); } real hypot(real x, real y) { return sqrt(x*x + y*y); } /* Compute an IEEE remainder */ real remainder(real x, real y) { if (y == 0) raise nan(); real q = x / y; int n; if (q < 0) n = ceil(q - 0.5); else n = floor(q + 0.5); if (abs(q-n) == 0.5) { if (n % 2 != 0) { if (n > 0) n--; else n++; } } return x - n * y; } real drem(real x, real y) { return remainder (x, y); } real copysign(real x, real y) { x = abs(x); if (y < 0) x = -x; return x; } bool isoddint(real x) { return x == floor(x) && (floor(x) & 1) == 1; } bool isevenint(real x) { return x == floor(x) && (floor(x) & 1) == 0; } bool isint(real x) { return x == floor(x); } /* Deal with the oddities of IEEE pow */ real powl(real x, real y) { if (x == 0 && isoddint(y) && y < 0) raise infinity(1); if (x == 0 && y < 0) raise infinity(1); if (x == 0 && y > 0) return 0; if (x == 1) return 1; if (y == 0) return 1; if (x < 0 && !isint(y)) raise nan(); return pow(x, y); } real scalb(real x, real y) { if (!isint(y)) raise nan(); return x * 2 ** y; } /* Functions of the form f(x,y) */ func_f_ff_t[] funcs_f_ff = { { .f = atan2, .name = "atan2l" }, { .f = powl, .name = "powl" }, { .f = fmod, .name = "fmodl" }, # { .f = nextafter, .name = "nextafterl" }, # { .f = nexttoward, .name = "nexttowardl" }, { .f = fdim, .name = "fdiml" }, { .f = fmax, .name = "fmaxl" }, { .f = fmin, .name = "fminl" }, { .f = hypot, .name = "hypotl" }, { .f = scalb, .name = "scalbl" }, { .f = remainder, .name = "remainderl" }, { .f = drem, .name = "dreml" }, { .f = copysign, .name = "copysignl" }, }; void gen_real_f_fi(func_f_fi_t f) { real x0, y; int x1; string vec = sprintf("%s_vec", f.name); printf("\n"); if (is_full_func(f.name)) printf("#ifdef FULL_LONG_DOUBLE\n"); string prec_name = make_prec(f.name); printf("long_double_test_f_fi_t %s[] = {\n", vec); for (x0 = -4; x0 <= 4; x0 += .25) { for (x1 = -16; x1 <= 16; x1 += 1) { try { string sy; try { try { y = imprecise(f.f(imprecise(x0, prec), x1), out_prec); sy = sprintf("%.-eL", y); } catch divide_by_zero(real x, real y) { if (x == 0) raise invalid_argument(f.name, 0, x); raise infinity(x); } } catch infinity(real v) { sy = "(long double) INFINITY"; if (v < 0) sy = "-" + sy; } catch nan() { sy = "(long double) NAN"; } printf(" { .line = __LINE__, .x0 = %.-eL, .x1 = %d, .y = %s },\n", x0, x1, sy); } catch invalid_argument(string s, int i, poly x) { } } } printf("};\n\n"); printf("int test_%s(void) {\n", f.name); printf(" unsigned int i;\n"); printf(" int result = 0;\n"); printf(" for (i = 0; i < sizeof(%s)/sizeof(%s[0]); i++) {\n", vec, vec); printf(" long double y = %s(%s[i].x0, %s[i].x1);\n", f.name, vec, vec); printf(" result += check_long_double(\"%s\", %s[i].line, %s, %s[i].y, y);\n", f.name, vec,prec_name, vec); printf(" }\n"); printf(" return result;\n"); printf("}\n"); if (is_full_func(f.name)) printf("#endif /* FULL_LONG_DOUBLE */\n"); } /* Functions of the form f(x,y) */ func_f_fi_t[] funcs_f_fi = { { .f = ldexpl, .name = "ldexpl" }, { .f = scalbnl, .name = "scalbnl" }, }; exception invalid_int(string y); void gen_real_i_f(func_i_f_t f) { real x; int y; string vec = sprintf("%s_vec", f.name); printf("\n"); if (is_full_func(f.name)) printf("#ifdef FULL_LONG_DOUBLE\n"); printf("long_double_test_i_f_t %s[] = {\n", vec); for (x = -10; x <= 10; x += .1) { try { string sy; try { y = f.f(imprecise(x, prec)); sy = sprintf("%d", y); } catch invalid_int(string s) { sy = s; } printf(" { .line = __LINE__, .x = %.-eL, .y = %s },\n", x, sy); } catch invalid_argument(string s, int i, poly x) { } } printf("};\n\n"); printf("int test_%s(void) {\n", f.name); printf(" unsigned int i;\n"); printf(" int result = 0;\n"); printf(" for (i = 0; i < sizeof(%s)/sizeof(%s[0]); i++) {\n", vec, vec); printf(" long long y = %s(%s[i].x);\n", f.name, vec); printf(" result += check_long_long(\"%s\", %s[i].line, %s[i].y, y);\n", f.name, vec, vec); printf(" }\n"); printf(" return result;\n"); printf("}\n"); if (is_full_func(f.name)) printf("#endif /* FULL_LONG_DOUBLE */\n"); } int finite(real x) { return 1; } int ilogb(real x) { if (x == 0) raise invalid_int("FP_ILOGB0"); return exponent(imprecise(x)) - 1; } int isinf(real x) { return 0; } int isnan(real x) { return 0; } int lrint(real x) { return rintl(x); } int lround(real x) { int ix = floor(x); real diff = x - ix; if ((diff == 0.5) && (x > 0) || (diff > 0.5)) ix++; return ix; } /* Functions of the form i(x) */ func_i_f_t[] funcs_i_f = { { .f = finite, .name = "finitel" }, { .f = ilogb, .name = "ilogb" }, { .f = isinf, .name = "isinfl" }, { .f = isnan, .name = "isnanl" }, { .f = lrint, .name = "lrintl" }, { .f = lrint, .name = "llrintl" }, { .f = lround, .name = "lroundl" }, { .f = lround, .name = "llroundl" }, }; /* * These functions aren't tested yet * * long double modfl (long double, long double *); * float nexttowardf (float, long double); * double nexttoward (double, long double); * long double nextowardl (long double, long double); * long double remquol (long double, long double, int *); * long double fmal (long double, long double, long double); * long double lgammal_r (long double, int *); * void sincosl (long double, long double *, long double *); */ void main() { for (int i = 0; i < dim(funcs_i_f); i++) gen_real_i_f(funcs_i_f[i]); for (int i = 0; i < dim(funcs_f_fi); i++) gen_real_f_fi(funcs_f_fi[i]); for (int i = 0; i < dim(funcs_f_ff); i++) gen_real_f_ff(funcs_f_ff[i]); for (int i = 0; i < dim(funcs_f_f); i++) gen_real_f_f(funcs_f_f[i]); printf("long_double_test_t long_double_tests[] = {\n"); for (int i = 0; i < dim(funcs_f_f); i++) { if (is_full_func(funcs_f_f[i].name)) printf("#ifdef FULL_LONG_DOUBLE\n"); printf(" { .name = \"%s\", .test = test_%s },\n", funcs_f_f[i].name, funcs_f_f[i].name); if (is_full_func(funcs_f_f[i].name)) printf("#endif /* FULL_LONG_DOUBLE */\n"); } for (int i = 0; i < dim(funcs_f_ff); i++) { if (is_full_func(funcs_f_ff[i].name)) printf("#ifdef FULL_LONG_DOUBLE\n"); printf(" { .name = \"%s\", .test = test_%s },\n", funcs_f_ff[i].name, funcs_f_ff[i].name); if (is_full_func(funcs_f_ff[i].name)) printf("#endif /* FULL_LONG_DOUBLE */\n"); } for (int i = 0; i < dim(funcs_f_fi); i++) { if (is_full_func(funcs_f_fi[i].name)) printf("#ifdef FULL_LONG_DOUBLE\n"); printf(" { .name = \"%s\", .test = test_%s },\n", funcs_f_fi[i].name, funcs_f_fi[i].name); if (is_full_func(funcs_f_fi[i].name)) printf("#endif /* FULL_LONG_DOUBLE */\n"); } for (int i = 0; i < dim(funcs_i_f); i++) { if (is_full_func(funcs_i_f[i].name)) printf("#ifdef FULL_LONG_DOUBLE\n"); printf(" { .name = \"%s\", .test = test_%s },\n", funcs_i_f[i].name, funcs_i_f[i].name); if (is_full_func(funcs_i_f[i].name)) printf("#endif /* FULL_LONG_DOUBLE */\n"); } printf("};\n"); } main();