From: Toni Wilen Date: Fri, 14 Apr 2017 08:07:44 +0000 (+0300) Subject: Transcendental softfloat function support. (Previous) X-Git-Tag: 3500~66 X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=9092e16528d98c3806fe8ee7111f34e58e9967b7;p=francis%2Fwinuae.git Transcendental softfloat function support. (Previous) --- diff --git a/fpp_softfloat.cpp b/fpp_softfloat.cpp index b6779ee2..35ab8270 100644 --- a/fpp_softfloat.cpp +++ b/fpp_softfloat.cpp @@ -185,108 +185,6 @@ static bool fp_is_unnormal(fpdata *fpd) return floatx80_is_unnormal(fpd->fpx) != 0; } -/* Functions for converting between float formats */ -static const fptype twoto32 = 4294967296.0; - -static void to_native(fptype *fp, fpdata *fpd) -{ - int expon; - fptype frac; - - expon = fpd->fpx.high & 0x7fff; - - if (fp_is_zero(fpd)) { - *fp = fp_is_neg(fpd) ? -0.0 : +0.0; - return; - } - if (fp_is_nan(fpd)) { -#if USE_LONG_DOUBLE - *fp = sqrtl(-1); -#else - *fp = sqrt(-1); -#endif - return; - } - if (fp_is_infinity(fpd)) { - double zero = 0.0; -#if USE_LONG_DOUBLE - *fp = fp_is_neg(fpd) ? logl(0.0) : (1.0 / zero); -#else - *fp = fp_is_neg(fpd) ? log(0.0) : (1.0 / zero); -#endif - return; - } - - frac = (fptype)fpd->fpx.low / (fptype)(twoto32 * 2147483648.0); - if (fp_is_neg(fpd)) - frac = -frac; -#if USE_LONG_DOUBLE - *fp = ldexpl (frac, expon - 16383); -#else - *fp = ldexp (frac, expon - 16383); -#endif -} - -static bool to_native_checked(fptype *fp, fpdata *fpd, fpdata *dst) -{ - uint64_t aSig = extractFloatx80Frac(fpd->fpx); - int32_t aExp = extractFloatx80Exp(fpd->fpx); - if (aExp == 0x7FFF && (uint64_t)(aSig << 1)) { - dst->fpx = propagateFloatx80NaN(fpd->fpx, fpd->fpx, &fs); - return true; - } - to_native(fp, fpd); - return false; -} - -static void from_native(fptype fp, fpdata *fpd) -{ - int expon; - fptype frac; - - if (signbit(fp)) - fpd->fpx.high = 0x8000; - else - fpd->fpx.high = 0x0000; - - if (isnan(fp)) { - fpd->fpx.high |= 0x7fff; - fpd->fpx.low = LIT64(0xffffffffffffffff); - return; - } - if (isinf(fp)) { - fpd->fpx.high |= 0x7fff; - fpd->fpx.low = LIT64(0x0000000000000000); - return; - } - if (fp == 0.0) { - fpd->fpx.low = LIT64(0x0000000000000000); - return; - } - if (fp < 0.0) - fp = -fp; - -#if USE_LONG_DOUBLE - frac = frexpl (fp, &expon); -#else - frac = frexp (fp, &expon); -#endif - frac += 0.5 / (twoto32 * twoto32); - if (frac >= 1.0) { - frac /= 2.0; - expon++; - } - fpd->fpx.high |= (expon + 16383 - 1) & 0x7fff; - fpd->fpx.low = (uint64_t)(frac * (fptype)(twoto32 * twoto32)); - - while (!(fpd->fpx.low & LIT64( 0x8000000000000000))) { - if (fpd->fpx.high == 0) { - break; - } - fpd->fpx.low <<= 1; - fpd->fpx.high--; - } -} static void to_single(fpdata *fpd, uae_u32 wrd1) { @@ -440,84 +338,7 @@ static void fp_intrz(fpdata *a, fpdata *b) { a->fpx = floatx80_round_to_int_toward_zero(b->fpx, &fs); } -static void fp_lognp1(fpdata *a, fpdata *b) -{ - fptype fpa; - flag e = 0; - a->fpx = floatx80_lognp1_check(b->fpx, &e, &fs); - if (e) - return; - to_native(&fpa, b); - fpa = logl(a->fp + 1.0); - from_native(fpa, a); -} -static void fp_sin(fpdata *a, fpdata *b) -{ - fptype fpa; - flag e = 0; - a->fpx = floatx80_sin_check(b->fpx, &e, &fs); - if (e) - return; - to_native(&fpa, b); - fpa = sinl(fpa); - from_native(fpa, a); -} -static void fp_tan(fpdata *a, fpdata *b) -{ - fptype fpa; - flag e = 0; - a->fpx = floatx80_tan_check(b->fpx, &e, &fs); - if (e) - return; - to_native(&fpa, b); - fpa = tanl(fpa); - from_native(fpa, a); -} -static void fp_logn(fpdata *a, fpdata *b) -{ - fptype fpa; - flag e = 0; - a->fpx = floatx80_logn_check(b->fpx, &e, &fs); - if (e) - return; - to_native(&fpa, b); - fpa = logl(fpa); - from_native(fpa, a); -} -static void fp_log10(fpdata *a, fpdata *b) -{ - fptype fpa; - flag e = 0; - a->fpx = floatx80_log10_check(b->fpx, &e, &fs); - if (e) - return; - to_native(&fpa, b); - fpa = log10l(fpa); - from_native(fpa, a); -} -static void fp_log2(fpdata *a, fpdata *b) -{ - fptype fpa; - flag e = 0; - a->fpx = floatx80_log2_check(b->fpx, &e, &fs); - if (e) - return; - to_native(&fpa, b); - fpa = log2l(fpa); - from_native(fpa, a); -} -static void fp_cos(fpdata *a, fpdata *b) -{ - fptype fpa; - flag e = 0; - a->fpx = floatx80_sin_check(b->fpx, &e, &fs); - if (e) - return; - to_native(&fpa, b); - fpa = cosl(fpa); - from_native(fpa, a); -} static void fp_getexp(fpdata *a, fpdata *b) { a->fpx = floatx80_getexp(b->fpx, &fs); @@ -616,7 +437,262 @@ static void fp_sqrt(fpdata *a, fpdata *b, int prec) } -/* FIXME: create softfloat functions for following arithmetics */ +static void fp_sinh(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_sinh(b->fpx, &fs); +} +static void fp_lognp1(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_lognp1(b->fpx, &fs); +} +static void fp_etoxm1(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_etoxm1(b->fpx, &fs); +} +static void fp_tanh(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_tanh(b->fpx, &fs); +} +static void fp_atan(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_atan(b->fpx, &fs); +} +static void fp_asin(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_asin(b->fpx, &fs); +} +static void fp_atanh(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_atanh(b->fpx, &fs); +} +static void fp_sin(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_sin(b->fpx, &fs); +} +static void fp_tan(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_tan(b->fpx, &fs); +} +static void fp_etox(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_etox(b->fpx, &fs); +} +static void fp_twotox(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_twotox(b->fpx, &fs); +} +static void fp_tentox(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_tentox(b->fpx, &fs); +} +static void fp_logn(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_logn(b->fpx, &fs); +} +static void fp_log10(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_log10(b->fpx, &fs); +} +static void fp_log2(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_log2(b->fpx, &fs); +} +static void fp_cosh(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_cosh(b->fpx, &fs); +} +static void fp_acos(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_acos(b->fpx, &fs); +} +static void fp_cos(fpdata *a, fpdata *b) +{ + a->fpx = floatx80_cos(b->fpx, &fs); +} + +/* Functions for converting between float formats */ +static const fptype twoto32 = 4294967296.0; + +static void to_native(fptype *fp, fpdata *fpd) +{ + int expon; + fptype frac; + + expon = fpd->fpx.high & 0x7fff; + + if (fp_is_zero(fpd)) { + *fp = fp_is_neg(fpd) ? -0.0 : +0.0; + return; + } + if (fp_is_nan(fpd)) { +#if USE_LONG_DOUBLE + *fp = sqrtl(-1); +#else + *fp = sqrt(-1); +#endif + return; + } + if (fp_is_infinity(fpd)) { + double zero = 0.0; +#if USE_LONG_DOUBLE + *fp = fp_is_neg(fpd) ? logl(0.0) : (1.0 / zero); +#else + *fp = fp_is_neg(fpd) ? log(0.0) : (1.0 / zero); +#endif + return; + } + + frac = (fptype)fpd->fpx.low / (fptype)(twoto32 * 2147483648.0); + if (fp_is_neg(fpd)) + frac = -frac; +#if USE_LONG_DOUBLE + *fp = ldexpl (frac, expon - 16383); +#else + *fp = ldexp (frac, expon - 16383); +#endif +} + +static void from_native(fptype fp, fpdata *fpd) +{ + int expon; + fptype frac; + + if (signbit(fp)) + fpd->fpx.high = 0x8000; + else + fpd->fpx.high = 0x0000; + + if (isnan(fp)) { + fpd->fpx.high |= 0x7fff; + fpd->fpx.low = LIT64(0xffffffffffffffff); + return; + } + if (isinf(fp)) { + fpd->fpx.high |= 0x7fff; + fpd->fpx.low = LIT64(0x0000000000000000); + return; + } + if (fp == 0.0) { + fpd->fpx.low = LIT64(0x0000000000000000); + return; + } + if (fp < 0.0) + fp = -fp; + +#if USE_LONG_DOUBLE + frac = frexpl (fp, &expon); +#else + frac = frexp (fp, &expon); +#endif + frac += 0.5 / (twoto32 * twoto32); + if (frac >= 1.0) { + frac /= 2.0; + expon++; + } + fpd->fpx.high |= (expon + 16383 - 1) & 0x7fff; + fpd->fpx.low = (uint64_t)(frac * (fptype)(twoto32 * twoto32)); + + while (!(fpd->fpx.low & LIT64( 0x8000000000000000))) { + if (fpd->fpx.high == 0) { + break; + } + fpd->fpx.low <<= 1; + fpd->fpx.high--; + } +} + +#if 0 /* Old fallback functions disabled */ + +static bool to_native_checked(fptype *fp, fpdata *fpd, fpdata *dst) +{ + uint64_t aSig = extractFloatx80Frac(fpd->fpx); + int32_t aExp = extractFloatx80Exp(fpd->fpx); + if (aExp == 0x7FFF && (uint64_t)(aSig << 1)) { + dst->fpx = propagateFloatx80NaN(fpd->fpx, fpd->fpx, &fs); + return true; + } + to_native(fp, fpd); + return false; +} + +static void fp_lognp1(fpdata *a, fpdata *b) +{ + fptype fpa; + flag e = 0; + a->fpx = floatx80_lognp1_check(b->fpx, &e, &fs); + if (e) + return; + to_native(&fpa, b); + fpa = logl(a->fp + 1.0); + from_native(fpa, a); +} +static void fp_sin(fpdata *a, fpdata *b) +{ + fptype fpa; + flag e = 0; + a->fpx = floatx80_sin_check(b->fpx, &e, &fs); + if (e) + return; + to_native(&fpa, b); + fpa = sinl(fpa); + from_native(fpa, a); +} +static void fp_tan(fpdata *a, fpdata *b) +{ + fptype fpa; + flag e = 0; + a->fpx = floatx80_tan_check(b->fpx, &e, &fs); + if (e) + return; + to_native(&fpa, b); + fpa = tanl(fpa); + from_native(fpa, a); +} +static void fp_logn(fpdata *a, fpdata *b) +{ + fptype fpa; + flag e = 0; + a->fpx = floatx80_logn_check(b->fpx, &e, &fs); + if (e) + return; + to_native(&fpa, b); + fpa = logl(fpa); + from_native(fpa, a); +} +static void fp_log10(fpdata *a, fpdata *b) +{ + fptype fpa; + flag e = 0; + a->fpx = floatx80_log10_check(b->fpx, &e, &fs); + if (e) + return; + to_native(&fpa, b); + fpa = log10l(fpa); + from_native(fpa, a); +} +static void fp_log2(fpdata *a, fpdata *b) +{ + fptype fpa; + flag e = 0; + a->fpx = floatx80_log2_check(b->fpx, &e, &fs); + if (e) + return; + to_native(&fpa, b); + fpa = log2l(fpa); + from_native(fpa, a); +} + +static void fp_cos(fpdata *a, fpdata *b) +{ + fptype fpa; + flag e = 0; + a->fpx = floatx80_sin_check(b->fpx, &e, &fs); + if (e) + return; + to_native(&fpa, b); + fpa = cosl(fpa); + from_native(fpa, a); +} static void fp_sinh(fpdata *a, fpdata *b) { @@ -751,6 +827,8 @@ static void fp_acos(fpdata *a, fpdata *b) fp_round(a); } +#endif + static void fp_normalize(fpdata *a) { a->fpx = floatx80_normalize(a->fpx); diff --git a/softfloat/softfloat.h b/softfloat/softfloat.h index 5ae05c60..0ec41d65 100644 --- a/softfloat/softfloat.h +++ b/softfloat/softfloat.h @@ -95,7 +95,7 @@ this code that are retained. */ typedef uint8_t flag; -#define LIT64( a ) a##LL +#define LIT64( a ) a##ULL /*---------------------------------------------------------------------------- | Software IEC/IEEE floating-point ordering relations @@ -150,11 +150,9 @@ typedef uint64_t float64; #define const_float64(x) (x) #endif typedef struct { - uint64_t low; uint16_t high; + uint64_t low; } floatx80; -#define make_floatx80(exp, mant) ((floatx80) { mant, exp }) -#define make_floatx80_init(exp, mant) { .low = mant, .high = exp } typedef struct { #ifdef HOST_WORDS_BIGENDIAN uint64_t high, low; @@ -162,8 +160,6 @@ typedef struct { uint64_t low, high; #endif } float128; -#define make_float128(high_, low_) ((float128) { .high = high_, .low = low_ }) -#define make_float128_init(high_, low_) { .high = high_, .low = low_ } /*---------------------------------------------------------------------------- | Software IEC/IEEE floating-point underflow tininess-detection mode. @@ -426,25 +422,25 @@ floatx80 floatx80_sgldiv( floatx80 a, floatx80 b, float_status *status ); floatx80 floatx80_cmp( floatx80 a, floatx80 b, float_status *status ); floatx80 floatx80_tst( floatx80 a, float_status *status ); -// functions are in softfloat_extension.c -floatx80 floatx80_acos_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_asin_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_atan_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_atanh_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_cos_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_cosh_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_etox_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_etoxm1_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_log10_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_log2_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_logn_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_lognp1_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_sin_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_sinh_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_tan_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_tanh_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_tentox_check(floatx80 a, flag *e, float_status *status); -floatx80 floatx80_twotox_check(floatx80 a, flag *e, float_status *status); +// functions are in softfloat_fpsp.c +floatx80 floatx80_acos(floatx80 a, float_status *status); +floatx80 floatx80_asin(floatx80 a, float_status *status); +floatx80 floatx80_atan(floatx80 a, float_status *status); +floatx80 floatx80_atanh(floatx80 a, float_status *status); +floatx80 floatx80_cos(floatx80 a, float_status *status); +floatx80 floatx80_cosh(floatx80 a, float_status *status); +floatx80 floatx80_etox(floatx80 a, float_status *status); +floatx80 floatx80_etoxm1(floatx80 a, float_status *status); +floatx80 floatx80_log10(floatx80 a, float_status *status); +floatx80 floatx80_log2(floatx80 a, float_status *status); +floatx80 floatx80_logn(floatx80 a, float_status *status); +floatx80 floatx80_lognp1(floatx80 a, float_status *status); +floatx80 floatx80_sin(floatx80 a, float_status *status); +floatx80 floatx80_sinh(floatx80 a, float_status *status); +floatx80 floatx80_tan(floatx80 a, float_status *status); +floatx80 floatx80_tanh(floatx80 a, float_status *status); +floatx80 floatx80_tentox(floatx80 a, float_status *status); +floatx80 floatx80_twotox(floatx80 a, float_status *status); #endif // functions originally internal to softfloat.c diff --git a/softfloat/softfloat_fpsp.cpp b/softfloat/softfloat_fpsp.cpp new file mode 100644 index 00000000..b2d69f39 --- /dev/null +++ b/softfloat/softfloat_fpsp.cpp @@ -0,0 +1,2268 @@ + +/*============================================================================ + + This C source file is an extension to the SoftFloat IEC/IEEE Floating-point + Arithmetic Package, Release 2a. + + Written by Andreas Grabher for Previous, NeXT Computer Emulator. + +=============================================================================*/ + +#include +#include + +#include "softfloat.h" +#include "softfloat-specialize.h" +#include "softfloat_fpsp_tables.h" + + +/*---------------------------------------------------------------------------- +| Algorithms for transcendental functions supported by MC68881 and MC68882 +| mathematical coprocessors. The functions are derived from FPSP library. +*----------------------------------------------------------------------------*/ + +#define pi_sig LIT64(0xc90fdaa22168c235) +#define pi_sig0 LIT64(0xc90fdaa22168c234) +#define pi_sig1 LIT64(0xc4c6628b80dc1cd1) + +#define pi_exp 0x4000 +#define piby2_exp 0x3FFF +#define piby4_exp 0x3FFE + +#define one_exp 0x3FFF +#define one_sig LIT64(0x8000000000000000) + + +/*---------------------------------------------------------------------------- + | Function for compactifying extended double-precision floating point values. + *----------------------------------------------------------------------------*/ + +int32_t floatx80_make_compact(int32_t aExp, uint64_t aSig) +{ + return (aExp<<16)|(aSig>>48); +} + + +/*---------------------------------------------------------------------------- + | Arc cosine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_acos(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1, one; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF && (uint64_t) (aSig<<1)) { + return propagateFloatx80NaNOneArg(a, status); + } + if (aExp == 0 && aSig == 0) { + float_raise(float_flag_inexact, status); + return roundAndPackFloatx80(status->floatx80_rounding_precision, 0, piby2_exp, pi_sig, 0, status); + } + + compact = floatx80_make_compact(aExp, aSig); + + if (compact >= 0x3FFF8000) { // |X| >= 1 + if (aExp == one_exp && aSig == one_sig) { // |X| == 1 + if (aSign) { // X == -1 + a = packFloatx80(0, pi_exp, pi_sig); + float_raise(float_flag_inexact, status); + return floatx80_move(a, status); + } else { // X == +1 + return packFloatx80(0, 0, 0); + } + } else { // |X| > 1 + float_raise(float_flag_invalid, status); + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; + return a; + } + } // |X| < 1 + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + one = packFloatx80(0, one_exp, one_sig); + fp0 = a; + + fp1 = floatx80_add(one, fp0, status); // 1 + X + fp0 = floatx80_sub(one, fp0, status); // 1 - X + fp0 = floatx80_div(fp0, fp1, status); // (1-X)/(1+X) + fp0 = floatx80_sqrt(fp0, status); // SQRT((1-X)/(1+X)) + fp0 = floatx80_atan(fp0, status); // ATAN(SQRT((1-X)/(1+X))) + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, fp0, status); // 2 * ATAN(SQRT((1-X)/(1+X))) + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | Arc sine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_asin(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1, fp2, one; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF && (uint64_t) (aSig<<1)) { + return propagateFloatx80NaNOneArg(a, status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + compact = floatx80_make_compact(aExp, aSig); + + if (compact >= 0x3FFF8000) { // |X| >= 1 + if (aExp == one_exp && aSig == one_sig) { // |X| == 1 + float_raise(float_flag_inexact, status); + a = packFloatx80(aSign, piby2_exp, pi_sig); + return floatx80_move(a, status); + } else { // |X| > 1 + float_raise(float_flag_invalid, status); + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; + return a; + } + + } // |X| < 1 + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + one = packFloatx80(0, one_exp, one_sig); + fp0 = a; + + fp1 = floatx80_sub(one, fp0, status); // 1 - X + fp2 = floatx80_add(one, fp0, status); // 1 + X + fp1 = floatx80_mul(fp2, fp1, status); // (1+X)*(1-X) + fp1 = floatx80_sqrt(fp1, status); // SQRT((1+X)*(1-X)) + fp0 = floatx80_div(fp0, fp1, status); // X/SQRT((1+X)*(1-X)) + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_atan(fp0, status); // ATAN(X/SQRT((1+X)*(1-X))) + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | Arc tangent + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_atan(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, tbl_index; + floatx80 fp0, fp1, fp2, fp3, xsave; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) return propagateFloatx80NaNOneArg(a, status); + a = packFloatx80(aSign, piby2_exp, pi_sig); + float_raise(float_flag_inexact, status); + return floatx80_move(a, status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + compact = floatx80_make_compact(aExp, aSig); + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + if (compact < 0x3FFB8000 || compact > 0x4002FFFF) { // |X| >= 16 or |X| < 1/16 + if (compact > 0x3FFF8000) { // |X| >= 16 + if (compact > 0x40638000) { // |X| > 2^(100) + fp0 = packFloatx80(aSign, piby2_exp, pi_sig); + fp1 = packFloatx80(aSign, 0x0001, one_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_sub(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + fp0 = a; + fp1 = packFloatx80(1, one_exp, one_sig); // -1 + fp1 = floatx80_div(fp1, fp0, status); // X' = -1/X + xsave = fp1; + fp0 = floatx80_mul(fp1, fp1, status); // Y = X'*X' + fp1 = floatx80_mul(fp0, fp0, status); // Z = Y*Y + fp3 = float64_to_floatx80(LIT64(0xBFB70BF398539E6A), status); // C5 + fp2 = float64_to_floatx80(LIT64(0x3FBC7187962D1D7D), status); // C4 + fp3 = floatx80_mul(fp3, fp1, status); // Z*C5 + fp2 = floatx80_mul(fp2, fp1, status); // Z*C4 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0xBFC24924827107B8), status), status); // C3+Z*C5 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3FC999999996263E), status), status); // C2+Z*C4 + fp1 = floatx80_mul(fp1, fp3, status); // Z*(C3+Z*C5) + fp2 = floatx80_mul(fp2, fp0, status); // Y*(C2+Z*C4) + fp1 = floatx80_add(fp1, float64_to_floatx80(LIT64(0xBFD5555555555536), status), status); // C1+Z*(C3+Z*C5) + fp0 = floatx80_mul(fp0, xsave, status); // X'*Y + fp1 = floatx80_add(fp1, fp2, status); // [Y*(C2+Z*C4)]+[C1+Z*(C3+Z*C5)] + fp0 = floatx80_mul(fp0, fp1, status); // X'*Y*([B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))]) ?? + fp0 = floatx80_add(fp0, xsave, status); + fp1 = packFloatx80(aSign, piby2_exp, pi_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } else { // |X| < 1/16 + if (compact < 0x3FD78000) { // |X| < 2^(-40) + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_move(a, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + fp0 = a; + xsave = a; + fp0 = floatx80_mul(fp0, fp0, status); // Y = X*X + fp1 = floatx80_mul(fp0, fp0, status); // Z = Y*Y + fp2 = float64_to_floatx80(LIT64(0x3FB344447F876989), status); // B6 + fp3 = float64_to_floatx80(LIT64(0xBFB744EE7FAF45DB), status); // B5 + fp2 = floatx80_mul(fp2, fp1, status); // Z*B6 + fp3 = floatx80_mul(fp3, fp1, status); // Z*B5 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3FBC71C646940220), status), status); // B4+Z*B6 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0xBFC24924921872F9), status), status); // B3+Z*B5 + fp2 = floatx80_mul(fp2, fp1, status); // Z*(B4+Z*B6) + fp1 = floatx80_mul(fp1, fp3, status); // Z*(B3+Z*B5) + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3FC9999999998FA9), status), status); // B2+Z*(B4+Z*B6) + fp1 = floatx80_add(fp1, float64_to_floatx80(LIT64(0xBFD5555555555555), status), status); // B1+Z*(B3+Z*B5) + fp2 = floatx80_mul(fp2, fp0, status); // Y*(B2+Z*(B4+Z*B6)) + fp0 = floatx80_mul(fp0, xsave, status); // X*Y + fp1 = floatx80_add(fp1, fp2, status); // [B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))] + fp0 = floatx80_mul(fp0, fp1, status); // X*Y*([B1+Z*(B3+Z*B5)]+[Y*(B2+Z*(B4+Z*B6))]) + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, xsave, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } + } else { + aSig &= LIT64(0xF800000000000000); + aSig |= LIT64(0x0400000000000000); + xsave = packFloatx80(aSign, aExp, aSig); // F + fp0 = a; + fp1 = a; // X + fp2 = packFloatx80(0, one_exp, one_sig); // 1 + fp1 = floatx80_mul(fp1, xsave, status); // X*F + fp0 = floatx80_sub(fp0, xsave, status); // X-F + fp1 = floatx80_add(fp1, fp2, status); // 1 + X*F + fp0 = floatx80_div(fp0, fp1, status); // U = (X-F)/(1+X*F) + + tbl_index = compact; + + tbl_index &= 0x7FFF0000; + tbl_index -= 0x3FFB0000; + tbl_index >>= 1; + tbl_index += compact&0x00007800; + tbl_index >>= 11; + + fp3 = atan_tbl[tbl_index]; + + fp3.high |= aSign ? 0x8000 : 0; // ATAN(F) + + fp1 = floatx80_mul(fp0, fp0, status); // V = U*U + fp2 = float64_to_floatx80(LIT64(0xBFF6687E314987D8), status); // A3 + fp2 = floatx80_add(fp2, fp1, status); // A3+V + fp2 = floatx80_mul(fp2, fp1, status); // V*(A3+V) + fp1 = floatx80_mul(fp1, fp0, status); // U*V + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x4002AC6934A26DB3), status), status); // A2+V*(A3+V) + fp1 = floatx80_mul(fp1, float64_to_floatx80(LIT64(0xBFC2476F4E1DA28E), status), status); // A1+U*V + fp1 = floatx80_mul(fp1, fp2, status); // A1*U*V*(A2+V*(A3+V)) + fp0 = floatx80_add(fp0, fp1, status); // ATAN(U) + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, fp3, status); // ATAN(X) + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | Hyperbolic arc tangent + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_atanh(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1, fp2, one; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF && (uint64_t) (aSig<<1)) { + return propagateFloatx80NaNOneArg(a, status); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + compact = floatx80_make_compact(aExp, aSig); + + if (compact >= 0x3FFF8000) { // |X| >= 1 + if (aExp == one_exp && aSig == one_sig) { // |X| == 1 + float_raise(float_flag_divbyzero, status); + return packFloatx80(aSign, 0x7FFF, floatx80_default_infinity_low); + } else { // |X| > 1 + float_raise(float_flag_invalid, status); + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; + return a; + } + } // |X| < 1 + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + one = packFloatx80(0, one_exp, one_sig); + fp2 = packFloatx80(aSign, 0x3FFE, one_sig); // SIGN(X) * (1/2) + fp0 = packFloatx80(0, aExp, aSig); // Y = |X| + fp1 = packFloatx80(1, aExp, aSig); // -Y + fp0 = floatx80_add(fp0, fp0, status); // 2Y + fp1 = floatx80_add(fp1, one, status); // 1-Y + fp0 = floatx80_div(fp0, fp1, status); // Z = 2Y/(1-Y) + fp0 = floatx80_lognp1(fp0, status); // LOG1P(Z) + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, fp2, status); // ATANH(X) = SIGN(X) * (1/2) * LOG1P(Z) + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | Cosine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_cos(floatx80 a, float_status *status) +{ + flag aSign, xSign; + int32_t aExp, xExp; + uint64_t aSig, xSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, l, n, j; + floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2; + float32 posneg1, twoto63; + flag adjn, endflag; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) return propagateFloatx80NaNOneArg(a, status); + float_raise(float_flag_invalid, status); + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; + return a; + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(0, one_exp, one_sig); + } + + adjn = 1; + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + compact = floatx80_make_compact(aExp, aSig); + + fp0 = a; + + if (compact < 0x3FD78000 || compact > 0x4004BC7E) { // 2^(-40) > |X| > 15 PI + if (compact > 0x3FFF8000) { // |X| >= 15 PI + // REDUCEX + fp1 = packFloatx80(0, 0, 0); + if (compact == 0x7FFEFFFF) { + twopi1 = packFloatx80(aSign ^ 1, 0x7FFE, LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(aSign ^ 1, 0x7FDC, LIT64(0x85A308D300000000)); + fp0 = floatx80_add(fp0, twopi1, status); + fp1 = fp0; + fp0 = floatx80_add(fp0, twopi2, status); + fp1 = floatx80_sub(fp1, fp0, status); + fp1 = floatx80_add(fp1, twopi2, status); + } + loop: + xSign = extractFloatx80Sign(fp0); + xExp = extractFloatx80Exp(fp0); + xExp -= 0x3FFF; + if (xExp <= 28) { + l = 0; + endflag = 1; + } else { + l = xExp - 27; + endflag = 0; + } + invtwopi = packFloatx80(0, 0x3FFE - l, LIT64(0xA2F9836E4E44152A)); // INVTWOPI + twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000)); + + twoto63 = 0x5F000000; + twoto63 |= xSign ? 0x80000000 : 0x00000000; // SIGN(INARG)*2^63 IN SGL + + fp2 = floatx80_mul(fp0, invtwopi, status); + fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status), status); // THE FRACTIONAL PART OF FP2 IS ROUNDED + fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status), status); // FP2 is N + fp4 = floatx80_mul(twopi1, fp2, status); // W = N*P1 + fp5 = floatx80_mul(twopi2, fp2, status); // w = N*P2 + fp3 = floatx80_add(fp4, fp5, status); // FP3 is P + fp4 = floatx80_sub(fp4, fp3, status); // W-P + fp0 = floatx80_sub(fp0, fp3, status); // FP0 is A := R - P + fp4 = floatx80_add(fp4, fp5, status); // FP4 is p = (W-P)+w + fp3 = fp0; // FP3 is A + fp1 = floatx80_sub(fp1, fp4, status); // FP1 is a := r - p + fp0 = floatx80_add(fp0, fp1, status); // FP0 is R := A+a + + if (endflag > 0) { + n = floatx80_to_int32(fp2, status); + goto sincont; + } + fp3 = floatx80_sub(fp3, fp0, status); // A-R + fp1 = floatx80_add(fp1, fp3, status); // FP1 is r := (A-R)+a + goto loop; + } else { + // SINSM + fp0 = float32_to_floatx80(0x3F800000, status); // 1 + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + if (adjn) { + // COSTINY + a = floatx80_sub(fp0, float32_to_floatx80(0x00800000, status), status); + } else { + // SINTINY + a = floatx80_move(a, status); + } + float_raise(float_flag_inexact, status); + + return a; + } + } else { + fp1 = floatx80_mul(fp0, float64_to_floatx80(LIT64(0x3FE45F306DC9C883), status), status); // X*2/PI + + n = floatx80_to_int32(fp1, status); + j = 32 + n; + + fp0 = floatx80_sub(fp0, pi_tbl[j], status); // X-Y1 + fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status), status); // FP0 IS R = (X-Y1)-Y2 + + sincont: + if ((n + adjn) & 1) { + // COSPOLY + fp0 = floatx80_mul(fp0, fp0, status); // FP0 IS S + fp1 = floatx80_mul(fp0, fp0, status); // FP1 IS T + fp2 = float64_to_floatx80(LIT64(0x3D2AC4D0D6011EE3), status); // B8 + fp3 = float64_to_floatx80(LIT64(0xBDA9396F9F45AC19), status); // B7 + + xSign = extractFloatx80Sign(fp0); // X IS S + xExp = extractFloatx80Exp(fp0); + xSig = extractFloatx80Frac(fp0); + + if (((n + adjn) >> 1) & 1) { + xSign ^= 1; + posneg1 = 0xBF800000; // -1 + } else { + xSign ^= 0; + posneg1 = 0x3F800000; // 1 + } // X IS NOW R'= SGN*R + + fp2 = floatx80_mul(fp2, fp1, status); // TB8 + fp3 = floatx80_mul(fp3, fp1, status); // TB7 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3E21EED90612C972), status), status); // B6+TB8 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0xBE927E4FB79D9FCF), status), status); // B5+TB7 + fp2 = floatx80_mul(fp2, fp1, status); // T(B6+TB8) + fp3 = floatx80_mul(fp3, fp1, status); // T(B5+TB7) + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3EFA01A01A01D423), status), status); // B4+T(B6+TB8) + fp4 = packFloatx80(1, 0x3FF5, LIT64(0xB60B60B60B61D438)); + fp3 = floatx80_add(fp3, fp4, status); // B3+T(B5+TB7) + fp2 = floatx80_mul(fp2, fp1, status); // T(B4+T(B6+TB8)) + fp1 = floatx80_mul(fp1, fp3, status); // T(B3+T(B5+TB7)) + fp4 = packFloatx80(0, 0x3FFA, LIT64(0xAAAAAAAAAAAAAB5E)); + fp2 = floatx80_add(fp2, fp4, status); // B2+T(B4+T(B6+TB8)) + fp1 = floatx80_add(fp1, float32_to_floatx80(0xBF000000, status), status); // B1+T(B3+T(B5+TB7)) + fp0 = floatx80_mul(fp0, fp2, status); // S(B2+T(B4+T(B6+TB8))) + fp0 = floatx80_add(fp0, fp1, status); // [B1+T(B3+T(B5+TB7))]+[S(B2+T(B4+T(B6+TB8)))] + + x = packFloatx80(xSign, xExp, xSig); + fp0 = floatx80_mul(fp0, x, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80(posneg1, status), status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + // SINPOLY + xSign = extractFloatx80Sign(fp0); // X IS R + xExp = extractFloatx80Exp(fp0); + xSig = extractFloatx80Frac(fp0); + + xSign ^= ((n + adjn) >> 1) & 1; // X IS NOW R'= SGN*R + + fp0 = floatx80_mul(fp0, fp0, status); // FP0 IS S + fp1 = floatx80_mul(fp0, fp0, status); // FP1 IS T + fp3 = float64_to_floatx80(LIT64(0xBD6AAA77CCC994F5), status); // A7 + fp2 = float64_to_floatx80(LIT64(0x3DE612097AAE8DA1), status); // A6 + fp3 = floatx80_mul(fp3, fp1, status); // T*A7 + fp2 = floatx80_mul(fp2, fp1, status); // T*A6 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0xBE5AE6452A118AE4), status), status); // A5+T*A7 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3EC71DE3A5341531), status), status); // A4+T*A6 + fp3 = floatx80_mul(fp3, fp1, status); // T(A5+TA7) + fp2 = floatx80_mul(fp2, fp1, status); // T(A4+TA6) + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0xBF2A01A01A018B59), status), status); // A3+T(A5+TA7) + fp4 = packFloatx80(0, 0x3FF8, LIT64(0x88888888888859AF)); + fp2 = floatx80_add(fp2, fp4, status); // A2+T(A4+TA6) + fp1 = floatx80_mul(fp1, fp3, status); // T(A3+T(A5+TA7)) + fp2 = floatx80_mul(fp2, fp0, status); // S(A2+T(A4+TA6)) + fp4 = packFloatx80(1, 0x3FFC, LIT64(0xAAAAAAAAAAAAAA99)); + fp1 = floatx80_add(fp1, fp4, status); // A1+T(A3+T(A5+TA7)) + fp1 = floatx80_add(fp1, fp2, status); // [A1+T(A3+T(A5+TA7))]+[S(A2+T(A4+TA6))] + + x = packFloatx80(xSign, xExp, xSig); + fp0 = floatx80_mul(fp0, x, status); // R'*S + fp0 = floatx80_mul(fp0, fp1, status); // SIN(R')-R' + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, x, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } +} + +/*---------------------------------------------------------------------------- + | Hyperbolic cosine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_cosh(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) return propagateFloatx80NaNOneArg(a, status); + return packFloatx80(0, 0x7FFF, floatx80_default_infinity_low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(0, one_exp, one_sig); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact > 0x400CB167) { + if (compact > 0x400CB2B3) { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + return roundAndPackFloatx80(status->floatx80_rounding_precision, 0, 0x8000, one_sig, 0, status); + } else { + fp0 = packFloatx80(0, aExp, aSig); + fp0 = floatx80_sub(fp0, float64_to_floatx80(LIT64(0x40C62D38D3D64634), status), status); + fp0 = floatx80_sub(fp0, float64_to_floatx80(LIT64(0x3D6F90AEB1E75CC7), status), status); + fp0 = floatx80_etox(fp0, status); + fp1 = packFloatx80(0, 0x7FFB, one_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } + + fp0 = packFloatx80(0, aExp, aSig); // |X| + fp0 = floatx80_etox(fp0, status); // EXP(|X|) + fp0 = floatx80_mul(fp0, float32_to_floatx80(0x3F000000, status), status); // (1/2)*EXP(|X|) + fp1 = float32_to_floatx80(0x3E800000, status); // 1/4 + fp1 = floatx80_div(fp1, fp0, status); // 1/(2*EXP(|X|)) + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | e to x + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_etox(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, n, j, k, m, m1; + floatx80 fp0, fp1, fp2, fp3, l2, scale, adjscale; + flag adjflag; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) return propagateFloatx80NaNOneArg(a, status); + if (aSign) return packFloatx80(0, 0, 0); + return packFloatx80(0, 0x7FFF, floatx80_default_infinity_low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(0, one_exp, one_sig); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + adjflag = 0; + + if (aExp >= 0x3FBE) { // |X| >= 2^(-65) + compact = floatx80_make_compact(aExp, aSig); + + if (compact < 0x400CB167) { // |X| < 16380 log2 + fp0 = a; + fp1 = a; + fp0 = floatx80_mul(fp0, float32_to_floatx80(0x42B8AA3B, status), status); // 64/log2 * X + adjflag = 0; + n = floatx80_to_int32(fp0, status); // int(64/log2*X) + fp0 = int32_to_floatx80(n); + + j = n & 0x3F; // J = N mod 64 + m = n / 64; // NOTE: this is really arithmetic right shift by 6 + if (n < 0 && j) { // arithmetic right shift is division and round towards minus infinity + m--; + } + m += 0x3FFF; // biased exponent of 2^(M) + + expcont1: + fp2 = fp0; // N + fp0 = floatx80_mul(fp0, float32_to_floatx80(0xBC317218, status), status); // N * L1, L1 = lead(-log2/64) + l2 = packFloatx80(0, 0x3FDC, LIT64(0x82E308654361C4C6)); + fp2 = floatx80_mul(fp2, l2, status); // N * L2, L1+L2 = -log2/64 + fp0 = floatx80_add(fp0, fp1, status); // X + N*L1 + fp0 = floatx80_add(fp0, fp2, status); // R + + fp1 = floatx80_mul(fp0, fp0, status); // S = R*R + fp2 = float32_to_floatx80(0x3AB60B70, status); // A5 + fp2 = floatx80_mul(fp2, fp1, status); // fp2 is S*A5 + fp3 = floatx80_mul(float32_to_floatx80(0x3C088895, status), fp1, status); // fp3 is S*A4 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3FA5555555554431), status), status); // fp2 is A3+S*A5 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0x3FC5555555554018), status), status); // fp3 is A2+S*A4 + fp2 = floatx80_mul(fp2, fp1, status); // fp2 is S*(A3+S*A5) + fp3 = floatx80_mul(fp3, fp1, status); // fp3 is S*(A2+S*A4) + fp2 = floatx80_add(fp2, float32_to_floatx80(0x3F000000, status), status); // fp2 is A1+S*(A3+S*A5) + fp3 = floatx80_mul(fp3, fp0, status); // fp3 IS R*S*(A2+S*A4) + fp2 = floatx80_mul(fp2, fp1, status); // fp2 IS S*(A1+S*(A3+S*A5)) + fp0 = floatx80_add(fp0, fp3, status); // fp0 IS R+R*S*(A2+S*A4) + fp0 = floatx80_add(fp0, fp2, status); // fp0 IS EXP(R) - 1 + + fp1 = exp_tbl[j]; + fp0 = floatx80_mul(fp0, fp1, status); // 2^(J/64)*(Exp(R)-1) + fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j], status), status); // accurate 2^(J/64) + fp0 = floatx80_add(fp0, fp1, status); // 2^(J/64) + 2^(J/64)*(Exp(R)-1) + + scale = packFloatx80(0, m, one_sig); + if (adjflag) { + adjscale = packFloatx80(0, m1, one_sig); + fp0 = floatx80_mul(fp0, adjscale, status); + } + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, scale, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { // |X| >= 16380 log2 + if (compact > 0x400CB27C) { // |X| >= 16480 log2 + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + if (aSign) { + a = roundAndPackFloatx80(status->floatx80_rounding_precision, 0, -0x1000, aSig, 0, status); + } else { + a = roundAndPackFloatx80(status->floatx80_rounding_precision, 0, 0x8000, aSig, 0, status); + } + float_raise(float_flag_inexact, status); + + return a; + } else { + fp0 = a; + fp1 = a; + fp0 = floatx80_mul(fp0, float32_to_floatx80(0x42B8AA3B, status), status); // 64/log2 * X + adjflag = 1; + n = floatx80_to_int32(fp0, status); // int(64/log2*X) + fp0 = int32_to_floatx80(n); + + j = n & 0x3F; // J = N mod 64 + k = n / 64; // NOTE: this is really arithmetic right shift by 6 + if (n < 0 && j) { // arithmetic right shift is division and round towards minus infinity + k--; + } + m1 = k / 2; // NOTE: this is really arithmetic right shift by 1 + if (k < 0 && (k & 1)) { // arithmetic right shift is division and round towards minus infinity + m1--; + } + m = k - m1; + m1 += 0x3FFF; // biased exponent of 2^(M1) + m += 0x3FFF; // biased exponent of 2^(M) + + goto expcont1; + } + } + } else { // |X| < 2^(-65) + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(a, float32_to_floatx80(0x3F800000, status), status); // 1 + X + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | e to x minus 1 + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_etoxm1(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, n, j, m, m1; + floatx80 fp0, fp1, fp2, fp3, l2, sc, onebysc; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) return propagateFloatx80NaNOneArg(a, status); + if (aSign) return packFloatx80(aSign, one_exp, one_sig); + return packFloatx80(0, 0x7FFF, floatx80_default_infinity_low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + if (aExp >= 0x3FFD) { // |X| >= 1/4 + compact = floatx80_make_compact(aExp, aSig); + + if (compact <= 0x4004C215) { // |X| <= 70 log2 + fp0 = a; + fp1 = a; + fp0 = floatx80_mul(fp0, float32_to_floatx80(0x42B8AA3B, status), status); // 64/log2 * X + n = floatx80_to_int32(fp0, status); // int(64/log2*X) + fp0 = int32_to_floatx80(n); + + j = n & 0x3F; // J = N mod 64 + m = n / 64; // NOTE: this is really arithmetic right shift by 6 + if (n < 0 && j) { // arithmetic right shift is division and round towards minus infinity + m--; + } + m1 = -m; + //m += 0x3FFF; // biased exponent of 2^(M) + //m1 += 0x3FFF; // biased exponent of -2^(-M) + + fp2 = fp0; // N + fp0 = floatx80_mul(fp0, float32_to_floatx80(0xBC317218, status), status); // N * L1, L1 = lead(-log2/64) + l2 = packFloatx80(0, 0x3FDC, LIT64(0x82E308654361C4C6)); + fp2 = floatx80_mul(fp2, l2, status); // N * L2, L1+L2 = -log2/64 + fp0 = floatx80_add(fp0, fp1, status); // X + N*L1 + fp0 = floatx80_add(fp0, fp2, status); // R + + fp1 = floatx80_mul(fp0, fp0, status); // S = R*R + fp2 = float32_to_floatx80(0x3950097B, status); // A6 + fp2 = floatx80_mul(fp2, fp1, status); // fp2 is S*A6 + fp3 = floatx80_mul(float32_to_floatx80(0x3AB60B6A, status), fp1, status); // fp3 is S*A5 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3F81111111174385), status), status); // fp2 IS A4+S*A6 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0x3FA5555555554F5A), status), status); // fp3 is A3+S*A5 + fp2 = floatx80_mul(fp2, fp1, status); // fp2 IS S*(A4+S*A6) + fp3 = floatx80_mul(fp3, fp1, status); // fp3 IS S*(A3+S*A5) + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3FC5555555555555), status), status); // fp2 IS A2+S*(A4+S*A6) + fp3 = floatx80_add(fp3, float32_to_floatx80(0x3F000000, status), status); // fp3 IS A1+S*(A3+S*A5) + fp2 = floatx80_mul(fp2, fp1, status); // fp2 IS S*(A2+S*(A4+S*A6)) + fp1 = floatx80_mul(fp1, fp3, status); // fp1 IS S*(A1+S*(A3+S*A5)) + fp2 = floatx80_mul(fp2, fp0, status); // fp2 IS R*S*(A2+S*(A4+S*A6)) + fp0 = floatx80_add(fp0, fp1, status); // fp0 IS R+S*(A1+S*(A3+S*A5)) + fp0 = floatx80_add(fp0, fp2, status); // fp0 IS EXP(R) - 1 + + fp0 = floatx80_mul(fp0, exp_tbl[j], status); // 2^(J/64)*(Exp(R)-1) + + if (m >= 64) { + fp1 = float32_to_floatx80(exp_tbl2[j], status); + onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); // -2^(-M) + fp1 = floatx80_add(fp1, onebysc, status); + fp0 = floatx80_add(fp0, fp1, status); + fp0 = floatx80_add(fp0, exp_tbl[j], status); + } else if (m < -3) { + fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j], status), status); + fp0 = floatx80_add(fp0, exp_tbl[j], status); + onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); // -2^(-M) + fp0 = floatx80_add(fp0, onebysc, status); + } else { // -3 <= m <= 63 + fp1 = exp_tbl[j]; + fp0 = floatx80_add(fp0, float32_to_floatx80(exp_tbl2[j], status), status); + onebysc = packFloatx80(1, m1 + 0x3FFF, one_sig); // -2^(-M) + fp1 = floatx80_add(fp1, onebysc, status); + fp0 = floatx80_add(fp0, fp1, status); + } + + sc = packFloatx80(0, m + 0x3FFF, one_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, sc, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { // |X| > 70 log2 + if (aSign) { + fp0 = float32_to_floatx80(0xBF800000, status); // -1 + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80(0x00800000, status), status); // -1 + 2^(-126) + + float_raise(float_flag_inexact, status); + + return a; + } else { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + return floatx80_etox(a, status); + } + } + } else { // |X| < 1/4 + if (aExp >= 0x3FBE) { + fp0 = a; + fp0 = floatx80_mul(fp0, fp0, status); // S = X*X + fp1 = float32_to_floatx80(0x2F30CAA8, status); // B12 + fp1 = floatx80_mul(fp1, fp0, status); // S * B12 + fp2 = float32_to_floatx80(0x310F8290, status); // B11 + fp1 = floatx80_add(fp1, float32_to_floatx80(0x32D73220, status), status); // B10 + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + fp2 = floatx80_add(fp2, float32_to_floatx80(0x3493F281, status), status); // B9 + fp1 = floatx80_add(fp1, float64_to_floatx80(LIT64(0x3EC71DE3A5774682), status), status); // B8 + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3EFA01A019D7CB68), status), status); // B7 + fp1 = floatx80_add(fp1, float64_to_floatx80(LIT64(0x3F2A01A01A019DF3), status), status); // B6 + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3F56C16C16C170E2), status), status); // B5 + fp1 = floatx80_add(fp1, float64_to_floatx80(LIT64(0x3F81111111111111), status), status); // B4 + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3FA5555555555555), status), status); // B3 + fp3 = packFloatx80(0, 0x3FFC, LIT64(0xAAAAAAAAAAAAAAAB)); + fp1 = floatx80_add(fp1, fp3, status); // B2 + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, fp0, status); + + fp2 = floatx80_mul(fp2, fp0, status); + fp1 = floatx80_mul(fp1, a, status); + + fp0 = floatx80_mul(fp0, float32_to_floatx80(0x3F000000, status), status); // S*B1 + fp1 = floatx80_add(fp1, fp2, status); // Q + fp0 = floatx80_add(fp0, fp1, status); // S*B1+Q + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, a, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { // |X| < 2^(-65) + sc = packFloatx80(1, 1, one_sig); + fp0 = a; + + if (aExp < 0x0033) { // |X| < 2^(-16382) + fp0 = floatx80_mul(fp0, float64_to_floatx80(LIT64(0x48B0000000000000), status), status); + fp0 = floatx80_add(fp0, sc, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, float64_to_floatx80(LIT64(0x3730000000000000), status), status); + } else { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, sc, status); + } + + float_raise(float_flag_inexact, status); + + return a; + } + } +} + +/*---------------------------------------------------------------------------- + | Log base 10 + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_log10(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + floatx80 fp0, fp1; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) propagateFloatx80NaNOneArg(a, status); + if (aSign == 0) + return packFloatx80(0, 0x7FFF, floatx80_default_infinity_low); + } + + if (aExp == 0 && aSig == 0) { + float_raise(float_flag_divbyzero, status); + return packFloatx80(1, 0x7FFF, floatx80_default_infinity_low); + } + + if (aSign) { + float_raise(float_flag_invalid, status); + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; + return a; + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + fp0 = floatx80_logn(a, status); + fp1 = packFloatx80(0, 0x3FFD, LIT64(0xDE5BD8A937287195)); // INV_L10 + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, fp1, status); // LOGN(X)*INV_L10 + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | Log base 2 + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_log2(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + floatx80 fp0, fp1; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) propagateFloatx80NaNOneArg(a, status); + if (aSign == 0) + return packFloatx80(0, 0x7FFF, floatx80_default_infinity_low); + } + + if (aExp == 0) { + if (aSig == 0) { + float_raise(float_flag_divbyzero, status); + return packFloatx80(1, 0x7FFF, floatx80_default_infinity_low); + } + normalizeFloatx80Subnormal(aSig, &aExp, &aSig); + } + + if (aSign) { + float_raise(float_flag_invalid, status); + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; + return a; + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + if (aSig == one_sig) { // X is 2^k + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = int32_to_floatx80(aExp-0x3FFF); + } else { + fp0 = floatx80_logn(a, status); + fp1 = packFloatx80(0, 0x3FFF, LIT64(0xB8AA3B295C17F0BC)); // INV_L2 + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, fp1, status); // LOGN(X)*INV_L2 + } + + float_raise(float_flag_inexact, status); + + return a; +} + +/*---------------------------------------------------------------------------- + | Log base e + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_logn(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig, fSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, j, k, adjk; + floatx80 fp0, fp1, fp2, fp3, f, logof2, klog2, saveu; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) propagateFloatx80NaNOneArg(a, status); + if (aSign == 0) + return packFloatx80(0, 0x7FFF, floatx80_default_infinity_low); + } + + adjk = 0; + + if (aExp == 0) { + if (aSig == 0) { // zero + float_raise(float_flag_divbyzero, status); + return packFloatx80(1, 0x7FFF, floatx80_default_infinity_low); + } +#if 1 + if ((aSig & one_sig) == 0) { // denormal + normalizeFloatx80Subnormal(aSig, &aExp, &aSig); + adjk = -100; + aExp += 100; + a = packFloatx80(aSign, aExp, aSig); + } +#else + normalizeFloatx80Subnormal(aSig, &aExp, &aSig); +#endif + } + + if (aSign) { + float_raise(float_flag_invalid, status); + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; + return a; + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact < 0x3FFEF07D || compact > 0x3FFF8841) { // |X| < 15/16 or |X| > 17/16 + k = aExp - 0x3FFF; + k += adjk; + fp1 = int32_to_floatx80(k); + + fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000); + j = (fSig >> 56) & 0x7E; // DISPLACEMENT FOR 1/F + + f = packFloatx80(0, 0x3FFF, fSig); // F + fp0 = packFloatx80(0, 0x3FFF, aSig); // Y + + fp0 = floatx80_sub(fp0, f, status); // Y-F + + // LP1CONT1 + fp0 = floatx80_mul(fp0, log_tbl[j], status); // FP0 IS U = (Y-F)/F + logof2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC)); + klog2 = floatx80_mul(fp1, logof2, status); // FP1 IS K*LOG2 + fp2 = floatx80_mul(fp0, fp0, status); // FP2 IS V=U*U + + fp3 = fp2; + fp1 = fp2; + + fp1 = floatx80_mul(fp1, float64_to_floatx80(LIT64(0x3FC2499AB5E4040B), status), status); // V*A6 + fp2 = floatx80_mul(fp2, float64_to_floatx80(LIT64(0xBFC555B5848CB7DB), status), status); // V*A5 + fp1 = floatx80_add(fp1, float64_to_floatx80(LIT64(0x3FC99999987D8730), status), status); // A4+V*A6 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0xBFCFFFFFFF6F7E97), status), status); // A3+V*A5 + fp1 = floatx80_mul(fp1, fp3, status); // V*(A4+V*A6) + fp2 = floatx80_mul(fp2, fp3, status); // V*(A3+V*A5) + fp1 = floatx80_add(fp1, float64_to_floatx80(LIT64(0x3FD55555555555A4), status), status); // A2+V*(A4+V*A6) + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0xBFE0000000000008), status), status); // A1+V*(A3+V*A5) + fp1 = floatx80_mul(fp1, fp3, status); // V*(A2+V*(A4+V*A6)) + fp2 = floatx80_mul(fp2, fp3, status); // V*(A1+V*(A3+V*A5)) + fp1 = floatx80_mul(fp1, fp0, status); // U*V*(A2+V*(A4+V*A6)) + fp0 = floatx80_add(fp0, fp2, status); // U+V*(A1+V*(A3+V*A5)) + + fp1 = floatx80_add(fp1, log_tbl[j+1], status); // LOG(F)+U*V*(A2+V*(A4+V*A6)) + fp0 = floatx80_add(fp0, fp1, status); // FP0 IS LOG(F) + LOG(1+U) + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, klog2, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { // |X-1| >= 1/16 + fp0 = a; + fp1 = a; + fp1 = floatx80_sub(fp1, float32_to_floatx80(0x3F800000, status), status); // FP1 IS X-1 + fp0 = floatx80_add(fp0, float32_to_floatx80(0x3F800000, status), status); // FP0 IS X+1 + fp1 = floatx80_add(fp1, fp1, status); // FP1 IS 2(X-1) + + // LP1CONT2 + fp1 = floatx80_div(fp1, fp0, status); // U + saveu = fp1; + fp0 = floatx80_mul(fp1, fp1, status); // FP0 IS V = U*U + fp1 = floatx80_mul(fp0, fp0, status); // FP1 IS W = V*V + + fp3 = float64_to_floatx80(LIT64(0x3F175496ADD7DAD6), status); // B5 + fp2 = float64_to_floatx80(LIT64(0x3F3C71C2FE80C7E0), status); // B4 + fp3 = floatx80_mul(fp3, fp1, status); // W*B5 + fp2 = floatx80_mul(fp2, fp1, status); // W*B4 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0x3F624924928BCCFF), status), status); // B3+W*B5 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3F899999999995EC), status), status); // B2+W*B4 + fp1 = floatx80_mul(fp1, fp3, status); // W*(B3+W*B5) + fp2 = floatx80_mul(fp2, fp0, status); // V*(B2+W*B4) + fp1 = floatx80_add(fp1, float64_to_floatx80(LIT64(0x3FB5555555555555), status), status); // B1+W*(B3+W*B5) + + fp0 = floatx80_mul(fp0, saveu, status); // FP0 IS U*V + fp1 = floatx80_add(fp1, fp2, status); // B1+W*(B3+W*B5) + V*(B2+W*B4) + fp0 = floatx80_mul(fp0, fp1, status); // U*V*( [B1+W*(B3+W*B5)] + [V*(B2+W*B4)] ) + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, saveu, status); + + if (!floatx80_is_zero(a)) { + float_raise(float_flag_inexact, status); + } + + return a; + } +} + +/*---------------------------------------------------------------------------- + | Log base e of x plus 1 + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_lognp1(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig, fSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, j, k; + floatx80 fp0, fp1, fp2, fp3, f, logof2, klog2, saveu; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) propagateFloatx80NaNOneArg(a, status); + if (aSign) { + float_raise(float_flag_invalid, status); + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; + return a; + } + return packFloatx80(0, 0x7FFF, floatx80_default_infinity_low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + if (aSign && aExp >= one_exp) { + if (aExp == one_exp && aSig == one_sig) { + float_raise(float_flag_divbyzero, status); + packFloatx80(aSign, 0x7FFF, floatx80_default_infinity_low); + } + float_raise(float_flag_invalid, status); + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; + return a; + } + + if (aExp < 0x3f99 || (aExp == 0x3f99 && aSig == one_sig)) { // <= min threshold + float_raise(float_flag_inexact, status); + return floatx80_move(a, status); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + compact = floatx80_make_compact(aExp, aSig); + + fp0 = a; // Z + fp1 = a; + + fp0 = floatx80_add(fp0, float32_to_floatx80(0x3F800000, status), status); // X = (1+Z) + + aExp = extractFloatx80Exp(fp0); + aSig = extractFloatx80Frac(fp0); + + compact = floatx80_make_compact(aExp, aSig); + + if (compact < 0x3FFE8000 || compact > 0x3FFFC000) { // |X| < 1/2 or |X| > 3/2 + k = aExp - 0x3FFF; + fp1 = int32_to_floatx80(k); + + fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000); + j = (fSig >> 56) & 0x7E; // DISPLACEMENT FOR 1/F + + f = packFloatx80(0, 0x3FFF, fSig); // F + fp0 = packFloatx80(0, 0x3FFF, aSig); // Y + + fp0 = floatx80_sub(fp0, f, status); // Y-F + + lp1cont1: + // LP1CONT1 + fp0 = floatx80_mul(fp0, log_tbl[j], status); // FP0 IS U = (Y-F)/F + logof2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC)); + klog2 = floatx80_mul(fp1, logof2, status); // FP1 IS K*LOG2 + fp2 = floatx80_mul(fp0, fp0, status); // FP2 IS V=U*U + + fp3 = fp2; + fp1 = fp2; + + fp1 = floatx80_mul(fp1, float64_to_floatx80(LIT64(0x3FC2499AB5E4040B), status), status); // V*A6 + fp2 = floatx80_mul(fp2, float64_to_floatx80(LIT64(0xBFC555B5848CB7DB), status), status); // V*A5 + fp1 = floatx80_add(fp1, float64_to_floatx80(LIT64(0x3FC99999987D8730), status), status); // A4+V*A6 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0xBFCFFFFFFF6F7E97), status), status); // A3+V*A5 + fp1 = floatx80_mul(fp1, fp3, status); // V*(A4+V*A6) + fp2 = floatx80_mul(fp2, fp3, status); // V*(A3+V*A5) + fp1 = floatx80_add(fp1, float64_to_floatx80(LIT64(0x3FD55555555555A4), status), status); // A2+V*(A4+V*A6) + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0xBFE0000000000008), status), status); // A1+V*(A3+V*A5) + fp1 = floatx80_mul(fp1, fp3, status); // V*(A2+V*(A4+V*A6)) + fp2 = floatx80_mul(fp2, fp3, status); // V*(A1+V*(A3+V*A5)) + fp1 = floatx80_mul(fp1, fp0, status); // U*V*(A2+V*(A4+V*A6)) + fp0 = floatx80_add(fp0, fp2, status); // U+V*(A1+V*(A3+V*A5)) + + fp1 = floatx80_add(fp1, log_tbl[j+1], status); // LOG(F)+U*V*(A2+V*(A4+V*A6)) + fp0 = floatx80_add(fp0, fp1, status); // FP0 IS LOG(F) + LOG(1+U) + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, klog2, status); + + float_raise(float_flag_inexact, status); + + return a; + } else if (compact < 0x3FFEF07D || compact > 0x3FFF8841) { // |X| < 1/16 or |X| > -1/16 + // LP1CARE + fSig = (aSig & LIT64(0xFE00000000000000)) | LIT64(0x0100000000000000); + f = packFloatx80(0, 0x3FFF, fSig); // F + j = (fSig >> 56) & 0x7E; // DISPLACEMENT FOR 1/F + + if (compact >= 0x3FFF8000) { // 1+Z >= 1 + // KISZERO + fp0 = floatx80_sub(float32_to_floatx80(0x3F800000, status), f, status); // 1-F + fp0 = floatx80_add(fp0, fp1, status); // FP0 IS Y-F = (1-F)+Z + fp1 = packFloatx80(0, 0, 0); // K = 0 + } else { + // KISNEG + fp0 = floatx80_sub(float32_to_floatx80(0x40000000, status), f, status); // 2-F + fp1 = floatx80_add(fp1, fp1, status); // 2Z + fp0 = floatx80_add(fp0, fp1, status); // FP0 IS Y-F = (2-F)+2Z + fp1 = packFloatx80(1, one_exp, one_sig); // K = -1 + } + goto lp1cont1; + } else { + // LP1ONE16 + fp1 = floatx80_add(fp1, fp1, status); // FP1 IS 2Z + fp0 = floatx80_add(fp0, float32_to_floatx80(0x3F800000, status), status); // FP0 IS 1+X + + // LP1CONT2 + fp1 = floatx80_div(fp1, fp0, status); // U + saveu = fp1; + fp0 = floatx80_mul(fp1, fp1, status); // FP0 IS V = U*U + fp1 = floatx80_mul(fp0, fp0, status); // FP1 IS W = V*V + + fp3 = float64_to_floatx80(LIT64(0x3F175496ADD7DAD6), status); // B5 + fp2 = float64_to_floatx80(LIT64(0x3F3C71C2FE80C7E0), status); // B4 + fp3 = floatx80_mul(fp3, fp1, status); // W*B5 + fp2 = floatx80_mul(fp2, fp1, status); // W*B4 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0x3F624924928BCCFF), status), status); // B3+W*B5 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3F899999999995EC), status), status); // B2+W*B4 + fp1 = floatx80_mul(fp1, fp3, status); // W*(B3+W*B5) + fp2 = floatx80_mul(fp2, fp0, status); // V*(B2+W*B4) + fp1 = floatx80_add(fp1, float64_to_floatx80(LIT64(0x3FB5555555555555), status), status); // B1+W*(B3+W*B5) + + fp0 = floatx80_mul(fp0, saveu, status); // FP0 IS U*V + fp1 = floatx80_add(fp1, fp2, status); // B1+W*(B3+W*B5) + V*(B2+W*B4) + fp0 = floatx80_mul(fp0, fp1, status); // U*V*( [B1+W*(B3+W*B5)] + [V*(B2+W*B4)] ) + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, saveu, status); + + if (!floatx80_is_zero(a)) { + float_raise(float_flag_inexact, status); + } + + return a; + } +} + +/*---------------------------------------------------------------------------- + | Sine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_sin(floatx80 a, float_status *status) +{ + flag aSign, xSign; + int32_t aExp, xExp; + uint64_t aSig, xSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, l, n, j; + floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2; + float32 posneg1, twoto63; + flag adjn, endflag; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) return propagateFloatx80NaNOneArg(a, status); + float_raise(float_flag_invalid, status); + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; + return a; + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + adjn = 0; + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + compact = floatx80_make_compact(aExp, aSig); + + fp0 = a; + + if (compact < 0x3FD78000 || compact > 0x4004BC7E) { // 2^(-40) > |X| > 15 PI + if (compact > 0x3FFF8000) { // |X| >= 15 PI + // REDUCEX + fp1 = packFloatx80(0, 0, 0); + if (compact == 0x7FFEFFFF) { + twopi1 = packFloatx80(aSign ^ 1, 0x7FFE, LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(aSign ^ 1, 0x7FDC, LIT64(0x85A308D300000000)); + fp0 = floatx80_add(fp0, twopi1, status); + fp1 = fp0; + fp0 = floatx80_add(fp0, twopi2, status); + fp1 = floatx80_sub(fp1, fp0, status); + fp1 = floatx80_add(fp1, twopi2, status); + } + loop: + xSign = extractFloatx80Sign(fp0); + xExp = extractFloatx80Exp(fp0); + xExp -= 0x3FFF; + if (xExp <= 28) { + l = 0; + endflag = 1; + } else { + l = xExp - 27; + endflag = 0; + } + invtwopi = packFloatx80(0, 0x3FFE - l, LIT64(0xA2F9836E4E44152A)); // INVTWOPI + twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000)); + + twoto63 = 0x5F000000; + twoto63 |= xSign ? 0x80000000 : 0x00000000; // SIGN(INARG)*2^63 IN SGL + + fp2 = floatx80_mul(fp0, invtwopi, status); + fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status), status); // THE FRACTIONAL PART OF FP2 IS ROUNDED + fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status), status); // FP2 is N + fp4 = floatx80_mul(twopi1, fp2, status); // W = N*P1 + fp5 = floatx80_mul(twopi2, fp2, status); // w = N*P2 + fp3 = floatx80_add(fp4, fp5, status); // FP3 is P + fp4 = floatx80_sub(fp4, fp3, status); // W-P + fp0 = floatx80_sub(fp0, fp3, status); // FP0 is A := R - P + fp4 = floatx80_add(fp4, fp5, status); // FP4 is p = (W-P)+w + fp3 = fp0; // FP3 is A + fp1 = floatx80_sub(fp1, fp4, status); // FP1 is a := r - p + fp0 = floatx80_add(fp0, fp1, status); // FP0 is R := A+a + + if (endflag > 0) { + n = floatx80_to_int32(fp2, status); + goto sincont; + } + fp3 = floatx80_sub(fp3, fp0, status); // A-R + fp1 = floatx80_add(fp1, fp3, status); // FP1 is r := (A-R)+a + goto loop; + } else { + // SINSM + fp0 = float32_to_floatx80(0x3F800000, status); // 1 + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + if (adjn) { + // COSTINY + a = floatx80_sub(fp0, float32_to_floatx80(0x00800000, status), status); + } else { + // SINTINY + a = floatx80_move(a, status); + } + float_raise(float_flag_inexact, status); + + return a; + } + } else { + fp1 = floatx80_mul(fp0, float64_to_floatx80(LIT64(0x3FE45F306DC9C883), status), status); // X*2/PI + + n = floatx80_to_int32(fp1, status); + j = 32 + n; + + fp0 = floatx80_sub(fp0, pi_tbl[j], status); // X-Y1 + fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status), status); // FP0 IS R = (X-Y1)-Y2 + + sincont: + if ((n + adjn) & 1) { + // COSPOLY + fp0 = floatx80_mul(fp0, fp0, status); // FP0 IS S + fp1 = floatx80_mul(fp0, fp0, status); // FP1 IS T + fp2 = float64_to_floatx80(LIT64(0x3D2AC4D0D6011EE3), status); // B8 + fp3 = float64_to_floatx80(LIT64(0xBDA9396F9F45AC19), status); // B7 + + xSign = extractFloatx80Sign(fp0); // X IS S + xExp = extractFloatx80Exp(fp0); + xSig = extractFloatx80Frac(fp0); + + if (((n + adjn) >> 1) & 1) { + xSign ^= 1; + posneg1 = 0xBF800000; // -1 + } else { + xSign ^= 0; + posneg1 = 0x3F800000; // 1 + } // X IS NOW R'= SGN*R + + fp2 = floatx80_mul(fp2, fp1, status); // TB8 + fp3 = floatx80_mul(fp3, fp1, status); // TB7 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3E21EED90612C972), status), status); // B6+TB8 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0xBE927E4FB79D9FCF), status), status); // B5+TB7 + fp2 = floatx80_mul(fp2, fp1, status); // T(B6+TB8) + fp3 = floatx80_mul(fp3, fp1, status); // T(B5+TB7) + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3EFA01A01A01D423), status), status); // B4+T(B6+TB8) + fp4 = packFloatx80(1, 0x3FF5, LIT64(0xB60B60B60B61D438)); + fp3 = floatx80_add(fp3, fp4, status); // B3+T(B5+TB7) + fp2 = floatx80_mul(fp2, fp1, status); // T(B4+T(B6+TB8)) + fp1 = floatx80_mul(fp1, fp3, status); // T(B3+T(B5+TB7)) + fp4 = packFloatx80(0, 0x3FFA, LIT64(0xAAAAAAAAAAAAAB5E)); + fp2 = floatx80_add(fp2, fp4, status); // B2+T(B4+T(B6+TB8)) + fp1 = floatx80_add(fp1, float32_to_floatx80(0xBF000000, status), status); // B1+T(B3+T(B5+TB7)) + fp0 = floatx80_mul(fp0, fp2, status); // S(B2+T(B4+T(B6+TB8))) + fp0 = floatx80_add(fp0, fp1, status); // [B1+T(B3+T(B5+TB7))]+[S(B2+T(B4+T(B6+TB8)))] + + x = packFloatx80(xSign, xExp, xSig); + fp0 = floatx80_mul(fp0, x, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80(posneg1, status), status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + // SINPOLY + xSign = extractFloatx80Sign(fp0); // X IS R + xExp = extractFloatx80Exp(fp0); + xSig = extractFloatx80Frac(fp0); + + xSign ^= ((n + adjn) >> 1) & 1; // X IS NOW R'= SGN*R + + fp0 = floatx80_mul(fp0, fp0, status); // FP0 IS S + fp1 = floatx80_mul(fp0, fp0, status); // FP1 IS T + fp3 = float64_to_floatx80(LIT64(0xBD6AAA77CCC994F5), status); // A7 + fp2 = float64_to_floatx80(LIT64(0x3DE612097AAE8DA1), status); // A6 + fp3 = floatx80_mul(fp3, fp1, status); // T*A7 + fp2 = floatx80_mul(fp2, fp1, status); // T*A6 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0xBE5AE6452A118AE4), status), status); // A5+T*A7 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3EC71DE3A5341531), status), status); // A4+T*A6 + fp3 = floatx80_mul(fp3, fp1, status); // T(A5+TA7) + fp2 = floatx80_mul(fp2, fp1, status); // T(A4+TA6) + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0xBF2A01A01A018B59), status), status); // A3+T(A5+TA7) + fp4 = packFloatx80(0, 0x3FF8, LIT64(0x88888888888859AF)); + fp2 = floatx80_add(fp2, fp4, status); // A2+T(A4+TA6) + fp1 = floatx80_mul(fp1, fp3, status); // T(A3+T(A5+TA7)) + fp2 = floatx80_mul(fp2, fp0, status); // S(A2+T(A4+TA6)) + fp4 = packFloatx80(1, 0x3FFC, LIT64(0xAAAAAAAAAAAAAA99)); + fp1 = floatx80_add(fp1, fp4, status); // A1+T(A3+T(A5+TA7)) + fp1 = floatx80_add(fp1, fp2, status); // [A1+T(A3+T(A5+TA7))]+[S(A2+T(A4+TA6))] + + x = packFloatx80(xSign, xExp, xSig); + fp0 = floatx80_mul(fp0, x, status); // R'*S + fp0 = floatx80_mul(fp0, fp1, status); // SIN(R')-R' + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, x, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } +} + +/*---------------------------------------------------------------------------- + | Hyperbolic sine + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_sinh(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1, fp2; + float32 fact; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) return propagateFloatx80NaNOneArg(a, status); + return packFloatx80(aSign, 0x7FFF, floatx80_default_infinity_low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact > 0x400CB167) { + // SINHBIG + if (compact > 0x400CB2B3) { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign, 0x8000, aSig, 0, status); + } else { + fp0 = floatx80_abs(a, status); // Y = |X| + fp0 = floatx80_sub(fp0, float64_to_floatx80(LIT64(0x40C62D38D3D64634), status), status); // (|X|-16381LOG2_LEAD) + fp0 = floatx80_sub(fp0, float64_to_floatx80(LIT64(0x3D6F90AEB1E75CC7), status), status); // |X| - 16381 LOG2, ACCURATE + fp0 = floatx80_etox(fp0, status); + fp2 = packFloatx80(aSign, 0x7FFB, one_sig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, fp2, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } else { // |X| < 16380 LOG2 + fp0 = floatx80_abs(a, status); // Y = |X| + fp0 = floatx80_etoxm1(fp0, status); // FP0 IS Z = EXPM1(Y) + fp1 = floatx80_add(fp0, float32_to_floatx80(0x3F800000, status), status); // 1+Z + fp2 = fp0; + fp0 = floatx80_div(fp0, fp1, status); // Z/(1+Z) + fp0 = floatx80_add(fp0, fp2, status); + + fact = 0x3F000000; + fact |= aSign ? 0x80000000 : 0x00000000; + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, float32_to_floatx80(fact, status), status); + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | Tangent + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_tan(floatx80 a, float_status *status) +{ + flag aSign, xSign; + int32_t aExp, xExp; + uint64_t aSig, xSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, l, n, j; + floatx80 fp0, fp1, fp2, fp3, fp4, fp5, invtwopi, twopi1, twopi2; + float32 twoto63; + flag endflag; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) return propagateFloatx80NaNOneArg(a, status); + float_raise(float_flag_invalid, status); + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; + return a; + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + compact = floatx80_make_compact(aExp, aSig); + + fp0 = a; + + if (compact < 0x3FD78000 || compact > 0x4004BC7E) { // 2^(-40) > |X| > 15 PI + if (compact > 0x3FFF8000) { // |X| >= 15 PI + // REDUCEX + fp1 = packFloatx80(0, 0, 0); + if (compact == 0x7FFEFFFF) { + twopi1 = packFloatx80(aSign ^ 1, 0x7FFE, LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(aSign ^ 1, 0x7FDC, LIT64(0x85A308D300000000)); + fp0 = floatx80_add(fp0, twopi1, status); + fp1 = fp0; + fp0 = floatx80_add(fp0, twopi2, status); + fp1 = floatx80_sub(fp1, fp0, status); + fp1 = floatx80_add(fp1, twopi2, status); + } + loop: + xSign = extractFloatx80Sign(fp0); + xExp = extractFloatx80Exp(fp0); + xExp -= 0x3FFF; + if (xExp <= 28) { + l = 0; + endflag = 1; + } else { + l = xExp - 27; + endflag = 0; + } + invtwopi = packFloatx80(0, 0x3FFE - l, LIT64(0xA2F9836E4E44152A)); // INVTWOPI + twopi1 = packFloatx80(0, 0x3FFF + l, LIT64(0xC90FDAA200000000)); + twopi2 = packFloatx80(0, 0x3FDD + l, LIT64(0x85A308D300000000)); + + twoto63 = 0x5F000000; + twoto63 |= xSign ? 0x80000000 : 0x00000000; // SIGN(INARG)*2^63 IN SGL + + fp2 = floatx80_mul(fp0, invtwopi, status); + fp2 = floatx80_add(fp2, float32_to_floatx80(twoto63, status), status); // THE FRACTIONAL PART OF FP2 IS ROUNDED + fp2 = floatx80_sub(fp2, float32_to_floatx80(twoto63, status), status); // FP2 is N + fp4 = floatx80_mul(twopi1, fp2, status); // W = N*P1 + fp5 = floatx80_mul(twopi2, fp2, status); // w = N*P2 + fp3 = floatx80_add(fp4, fp5, status); // FP3 is P + fp4 = floatx80_sub(fp4, fp3, status); // W-P + fp0 = floatx80_sub(fp0, fp3, status); // FP0 is A := R - P + fp4 = floatx80_add(fp4, fp5, status); // FP4 is p = (W-P)+w + fp3 = fp0; // FP3 is A + fp1 = floatx80_sub(fp1, fp4, status); // FP1 is a := r - p + fp0 = floatx80_add(fp0, fp1, status); // FP0 is R := A+a + + if (endflag > 0) { + n = floatx80_to_int32(fp2, status); + goto tancont; + } + fp3 = floatx80_sub(fp3, fp0, status); // A-R + fp1 = floatx80_add(fp1, fp3, status); // FP1 is r := (A-R)+a + goto loop; + } else { + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_move(a, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } else { + fp1 = floatx80_mul(fp0, float64_to_floatx80(LIT64(0x3FE45F306DC9C883), status), status); // X*2/PI + + n = floatx80_to_int32(fp1, status); + j = 32 + n; + + fp0 = floatx80_sub(fp0, pi_tbl[j], status); // X-Y1 + fp0 = floatx80_sub(fp0, float32_to_floatx80(pi_tbl2[j], status), status); // FP0 IS R = (X-Y1)-Y2 + + tancont: + if (n & 1) { + // NODD + fp1 = fp0; // R + fp0 = floatx80_mul(fp0, fp0, status); // S = R*R + fp3 = float64_to_floatx80(LIT64(0x3EA0B759F50F8688), status); // Q4 + fp2 = float64_to_floatx80(LIT64(0xBEF2BAA5A8924F04), status); // P3 + fp3 = floatx80_mul(fp3, fp0, status); // SQ4 + fp2 = floatx80_mul(fp2, fp0, status); // SP3 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0xBF346F59B39BA65F), status), status); // Q3+SQ4 + fp4 = packFloatx80(0, 0x3FF6, LIT64(0xE073D3FC199C4A00)); + fp2 = floatx80_add(fp2, fp4, status); // P2+SP3 + fp3 = floatx80_mul(fp3, fp0, status); // S(Q3+SQ4) + fp2 = floatx80_mul(fp2, fp0, status); // S(P2+SP3) + fp4 = packFloatx80(0, 0x3FF9, LIT64(0xD23CD68415D95FA1)); + fp3 = floatx80_add(fp3, fp4, status); // Q2+S(Q3+SQ4) + fp4 = packFloatx80(1, 0x3FFC, LIT64(0x8895A6C5FB423BCA)); + fp2 = floatx80_add(fp2, fp4, status); // P1+S(P2+SP3) + fp3 = floatx80_mul(fp3, fp0, status); // S(Q2+S(Q3+SQ4)) + fp2 = floatx80_mul(fp2, fp0, status); // S(P1+S(P2+SP3)) + fp4 = packFloatx80(1, 0x3FFD, LIT64(0xEEF57E0DA84BC8CE)); + fp3 = floatx80_add(fp3, fp4, status); // Q1+S(Q2+S(Q3+SQ4)) + fp2 = floatx80_mul(fp2, fp1, status); // RS(P1+S(P2+SP3)) + fp0 = floatx80_mul(fp0, fp3, status); // S(Q1+S(Q2+S(Q3+SQ4))) + fp1 = floatx80_add(fp1, fp2, status); // R+RS(P1+S(P2+SP3)) + fp0 = floatx80_add(fp0, float32_to_floatx80(0x3F800000, status), status); // 1+S(Q1+S(Q2+S(Q3+SQ4))) + + xSign = extractFloatx80Sign(fp1); + xExp = extractFloatx80Exp(fp1); + xSig = extractFloatx80Frac(fp1); + xSign ^= 1; + fp1 = packFloatx80(xSign, xExp, xSig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_div(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + fp1 = floatx80_mul(fp0, fp0, status); // S = R*R + fp3 = float64_to_floatx80(LIT64(0x3EA0B759F50F8688), status); // Q4 + fp2 = float64_to_floatx80(LIT64(0xBEF2BAA5A8924F04), status); // P3 + fp3 = floatx80_mul(fp3, fp1, status); // SQ4 + fp2 = floatx80_mul(fp2, fp1, status); // SP3 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0xBF346F59B39BA65F), status), status); // Q3+SQ4 + fp4 = packFloatx80(0, 0x3FF6, LIT64(0xE073D3FC199C4A00)); + fp2 = floatx80_add(fp2, fp4, status); // P2+SP3 + fp3 = floatx80_mul(fp3, fp1, status); // S(Q3+SQ4) + fp2 = floatx80_mul(fp2, fp1, status); // S(P2+SP3) + fp4 = packFloatx80(0, 0x3FF9, LIT64(0xD23CD68415D95FA1)); + fp3 = floatx80_add(fp3, fp4, status); // Q2+S(Q3+SQ4) + fp4 = packFloatx80(1, 0x3FFC, LIT64(0x8895A6C5FB423BCA)); + fp2 = floatx80_add(fp2, fp4, status); // P1+S(P2+SP3) + fp3 = floatx80_mul(fp3, fp1, status); // S(Q2+S(Q3+SQ4)) + fp2 = floatx80_mul(fp2, fp1, status); // S(P1+S(P2+SP3)) + fp4 = packFloatx80(1, 0x3FFD, LIT64(0xEEF57E0DA84BC8CE)); + fp3 = floatx80_add(fp3, fp4, status); // Q1+S(Q2+S(Q3+SQ4)) + fp2 = floatx80_mul(fp2, fp0, status); // RS(P1+S(P2+SP3)) + fp1 = floatx80_mul(fp1, fp3, status); // S(Q1+S(Q2+S(Q3+SQ4))) + fp0 = floatx80_add(fp0, fp2, status); // R+RS(P1+S(P2+SP3)) + fp1 = floatx80_add(fp1, float32_to_floatx80(0x3F800000, status), status); // 1+S(Q1+S(Q2+S(Q3+SQ4))) + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_div(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } +} + +/*---------------------------------------------------------------------------- + | Hyperbolic tangent + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_tanh(floatx80 a, float_status *status) +{ + flag aSign, vSign; + int32_t aExp, vExp; + uint64_t aSig, vSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact; + floatx80 fp0, fp1; + float32 sign; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) return propagateFloatx80NaNOneArg(a, status); + return packFloatx80(aSign, one_exp, one_sig); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(aSign, 0, 0); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact < 0x3FD78000 || compact > 0x3FFFDDCE) { + // TANHBORS + if (compact < 0x3FFF8000) { + // TANHSM + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_move(a, status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + if (compact > 0x40048AA1) { + // TANHHUGE + sign = 0x3F800000; + sign |= aSign ? 0x80000000 : 0x00000000; + fp0 = float32_to_floatx80(sign, status); + sign &= 0x80000000; + sign ^= 0x80800000; // -SIGN(X)*EPS + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80(sign, status), status); + + float_raise(float_flag_inexact, status); + + return a; + } else { + fp0 = packFloatx80(0, aExp+1, aSig); // Y = 2|X| + fp0 = floatx80_etox(fp0, status); // FP0 IS EXP(Y) + fp0 = floatx80_add(fp0, float32_to_floatx80(0x3F800000, status), status); // EXP(Y)+1 + sign = aSign ? 0x80000000 : 0x00000000; + fp1 = floatx80_div(float32_to_floatx80(sign^0xC0000000, status), fp0, status); // -SIGN(X)*2 / [EXP(Y)+1] + fp0 = float32_to_floatx80(sign | 0x3F800000, status); // SIGN + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp1, fp0, status); + + float_raise(float_flag_inexact, status); + + return a; + } + } + } else { // 2**(-40) < |X| < (5/2)LOG2 + fp0 = packFloatx80(0, aExp+1, aSig); // Y = 2|X| + fp0 = floatx80_etoxm1(fp0, status); // FP0 IS Z = EXPM1(Y) + fp1 = floatx80_add(fp0, float32_to_floatx80(0x40000000, status), status); // Z+2 + + vSign = extractFloatx80Sign(fp1); + vExp = extractFloatx80Exp(fp1); + vSig = extractFloatx80Frac(fp1); + + fp1 = packFloatx80(vSign ^ aSign, vExp, vSig); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_div(fp0, fp1, status); + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | 10 to x + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_tentox(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, n, j, l, m, m1; + floatx80 fp0, fp1, fp2, fp3, adjfact, fact1, fact2; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) return propagateFloatx80NaNOneArg(a, status); + if (aSign) return packFloatx80(0, 0, 0); + return packFloatx80(0, 0x7FFF, floatx80_default_infinity_low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(0, one_exp, one_sig); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + fp0 = a; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact < 0x3FB98000 || compact > 0x400B9B07) { // |X| > 16480 LOG2/LOG10 or |X| < 2^(-70) + if (compact > 0x3FFF8000) { // |X| > 16480 + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + if (aSign) { + return roundAndPackFloatx80(status->floatx80_rounding_precision, 0, -0x1000, aSig, 0, status); + } else { + return roundAndPackFloatx80(status->floatx80_rounding_precision, 0, 0x8000, aSig, 0, status); + } + } else { // |X| < 2^(-70) + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80(0x3F800000, status), status); // 1 + X + + float_raise(float_flag_inexact, status); + + return a; + } + } else { // 2^(-70) <= |X| <= 16480 LOG 2 / LOG 10 + fp1 = fp0; // X + fp1 = floatx80_mul(fp1, float64_to_floatx80(LIT64(0x406A934F0979A371), status), status); // X*64*LOG10/LOG2 + n = floatx80_to_int32(fp1, status); // N=INT(X*64*LOG10/LOG2) + fp1 = int32_to_floatx80(n); + + j = n & 0x3F; + l = n / 64; // NOTE: this is really arithmetic right shift by 6 + if (n < 0 && j) { // arithmetic right shift is division and round towards minus infinity + l--; + } + m = l / 2; // NOTE: this is really arithmetic right shift by 1 + if (l < 0 && (l & 1)) { // arithmetic right shift is division and round towards minus infinity + m--; + } + m1 = l - m; + m1 += 0x3FFF; // ADJFACT IS 2^(M') + + adjfact = packFloatx80(0, m1, one_sig); + fact1 = exp2_tbl[j]; + fact1.high += m; + fact2.high = exp2_tbl2[j]>>16; + fact2.high += m; + fact2.low = (uint64_t)(exp2_tbl2[j] & 0xFFFF); + fact2.low <<= 48; + + fp2 = fp1; // N + fp1 = floatx80_mul(fp1, float64_to_floatx80(LIT64(0x3F734413509F8000), status), status); // N*(LOG2/64LOG10)_LEAD + fp3 = packFloatx80(1, 0x3FCD, LIT64(0xC0219DC1DA994FD2)); + fp2 = floatx80_mul(fp2, fp3, status); // N*(LOG2/64LOG10)_TRAIL + fp0 = floatx80_sub(fp0, fp1, status); // X - N L_LEAD + fp0 = floatx80_sub(fp0, fp2, status); // X - N L_TRAIL + fp2 = packFloatx80(0, 0x4000, LIT64(0x935D8DDDAAA8AC17)); // LOG10 + fp0 = floatx80_mul(fp0, fp2, status); // R + + // EXPR + fp1 = floatx80_mul(fp0, fp0, status); // S = R*R + fp2 = float64_to_floatx80(LIT64(0x3F56C16D6F7BD0B2), status); // A5 + fp3 = float64_to_floatx80(LIT64(0x3F811112302C712C), status); // A4 + fp2 = floatx80_mul(fp2, fp1, status); // S*A5 + fp3 = floatx80_mul(fp3, fp1, status); // S*A4 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3FA5555555554CC1), status), status); // A3+S*A5 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0x3FC5555555554A54), status), status); // A2+S*A4 + fp2 = floatx80_mul(fp2, fp1, status); // S*(A3+S*A5) + fp3 = floatx80_mul(fp3, fp1, status); // S*(A2+S*A4) + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3FE0000000000000), status), status); // A1+S*(A3+S*A5) + fp3 = floatx80_mul(fp3, fp0, status); // R*S*(A2+S*A4) + + fp2 = floatx80_mul(fp2, fp1, status); // S*(A1+S*(A3+S*A5)) + fp0 = floatx80_add(fp0, fp3, status); // R+R*S*(A2+S*A4) + fp0 = floatx80_add(fp0, fp2, status); // EXP(R) - 1 + + fp0 = floatx80_mul(fp0, fact1, status); + fp0 = floatx80_add(fp0, fact2, status); + fp0 = floatx80_add(fp0, fact1, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, adjfact, status); + + float_raise(float_flag_inexact, status); + + return a; + } +} + +/*---------------------------------------------------------------------------- + | 2 to x + *----------------------------------------------------------------------------*/ + +floatx80 floatx80_twotox(floatx80 a, float_status *status) +{ + flag aSign; + int32_t aExp; + uint64_t aSig; + + int8_t user_rnd_mode, user_rnd_prec; + + int32_t compact, n, j, l, m, m1; + floatx80 fp0, fp1, fp2, fp3, adjfact, fact1, fact2; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if ((uint64_t) (aSig<<1)) return propagateFloatx80NaNOneArg(a, status); + if (aSign) return packFloatx80(0, 0, 0); + return packFloatx80(0, 0x7FFF, floatx80_default_infinity_low); + } + + if (aExp == 0 && aSig == 0) { + return packFloatx80(0, one_exp, one_sig); + } + + user_rnd_mode = status->float_rounding_mode; + user_rnd_prec = status->floatx80_rounding_precision; + + fp0 = a; + + compact = floatx80_make_compact(aExp, aSig); + + if (compact < 0x3FB98000 || compact > 0x400D80C0) { // |X| > 16480 or |X| < 2^(-70) + if (compact > 0x3FFF8000) { // |X| > 16480 + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + if (aSign) { + return roundAndPackFloatx80(status->floatx80_rounding_precision, 0, -0x1000, aSig, 0, status); + } else { + return roundAndPackFloatx80(status->floatx80_rounding_precision, 0, 0x8000, aSig, 0, status); + } + } else { // |X| < 2^(-70) + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_add(fp0, float32_to_floatx80(0x3F800000, status), status); // 1 + X + + float_raise(float_flag_inexact, status); + + return a; + } + } else { // 2^(-70) <= |X| <= 16480 + fp1 = fp0; // X + fp1 = floatx80_mul(fp1, float32_to_floatx80(0x42800000, status), status); // X * 64 + n = floatx80_to_int32(fp1, status); + fp1 = int32_to_floatx80(n); + j = n & 0x3F; + l = n / 64; // NOTE: this is really arithmetic right shift by 6 + if (n < 0 && j) { // arithmetic right shift is division and round towards minus infinity + l--; + } + m = l / 2; // NOTE: this is really arithmetic right shift by 1 + if (l < 0 && (l & 1)) { // arithmetic right shift is division and round towards minus infinity + m--; + } + m1 = l - m; + m1 += 0x3FFF; // ADJFACT IS 2^(M') + + adjfact = packFloatx80(0, m1, one_sig); + fact1 = exp2_tbl[j]; + fact1.high += m; + fact2.high = exp2_tbl2[j]>>16; + fact2.high += m; + fact2.low = (uint64_t)(exp2_tbl2[j] & 0xFFFF); + fact2.low <<= 48; + + fp1 = floatx80_mul(fp1, float32_to_floatx80(0x3C800000, status), status); // (1/64)*N + fp0 = floatx80_sub(fp0, fp1, status); // X - (1/64)*INT(64 X) + fp2 = packFloatx80(0, 0x3FFE, LIT64(0xB17217F7D1CF79AC)); // LOG2 + fp0 = floatx80_mul(fp0, fp2, status); // R + + // EXPR + fp1 = floatx80_mul(fp0, fp0, status); // S = R*R + fp2 = float64_to_floatx80(LIT64(0x3F56C16D6F7BD0B2), status); // A5 + fp3 = float64_to_floatx80(LIT64(0x3F811112302C712C), status); // A4 + fp2 = floatx80_mul(fp2, fp1, status); // S*A5 + fp3 = floatx80_mul(fp3, fp1, status); // S*A4 + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3FA5555555554CC1), status), status); // A3+S*A5 + fp3 = floatx80_add(fp3, float64_to_floatx80(LIT64(0x3FC5555555554A54), status), status); // A2+S*A4 + fp2 = floatx80_mul(fp2, fp1, status); // S*(A3+S*A5) + fp3 = floatx80_mul(fp3, fp1, status); // S*(A2+S*A4) + fp2 = floatx80_add(fp2, float64_to_floatx80(LIT64(0x3FE0000000000000), status), status); // A1+S*(A3+S*A5) + fp3 = floatx80_mul(fp3, fp0, status); // R*S*(A2+S*A4) + + fp2 = floatx80_mul(fp2, fp1, status); // S*(A1+S*(A3+S*A5)) + fp0 = floatx80_add(fp0, fp3, status); // R+R*S*(A2+S*A4) + fp0 = floatx80_add(fp0, fp2, status); // EXP(R) - 1 + + fp0 = floatx80_mul(fp0, fact1, status); + fp0 = floatx80_add(fp0, fact2, status); + fp0 = floatx80_add(fp0, fact1, status); + + status->float_rounding_mode = user_rnd_mode; + status->floatx80_rounding_precision = user_rnd_prec; + + a = floatx80_mul(fp0, adjfact, status); + + float_raise(float_flag_inexact, status); + + return a; + } +} diff --git a/softfloat/softfloat_fpsp_tables.h b/softfloat/softfloat_fpsp_tables.h new file mode 100644 index 00000000..4f860132 --- /dev/null +++ b/softfloat/softfloat_fpsp_tables.h @@ -0,0 +1,528 @@ + +static const floatx80 atan_tbl[128] = { + {0x3FFB, LIT64(0x83D152C5060B7A51)}, + {0x3FFB, LIT64(0x8BC8544565498B8B)}, + {0x3FFB, LIT64(0x93BE406017626B0D)}, + {0x3FFB, LIT64(0x9BB3078D35AEC202)}, + {0x3FFB, LIT64(0xA3A69A525DDCE7DE)}, + {0x3FFB, LIT64(0xAB98E94362765619)}, + {0x3FFB, LIT64(0xB389E502F9C59862)}, + {0x3FFB, LIT64(0xBB797E436B09E6FB)}, + {0x3FFB, LIT64(0xC367A5C739E5F446)}, + {0x3FFB, LIT64(0xCB544C61CFF7D5C6)}, + {0x3FFB, LIT64(0xD33F62F82488533E)}, + {0x3FFB, LIT64(0xDB28DA8162404C77)}, + {0x3FFB, LIT64(0xE310A4078AD34F18)}, + {0x3FFB, LIT64(0xEAF6B0A8188EE1EB)}, + {0x3FFB, LIT64(0xF2DAF1949DBE79D5)}, + {0x3FFB, LIT64(0xFABD581361D47E3E)}, + {0x3FFC, LIT64(0x8346AC210959ECC4)}, + {0x3FFC, LIT64(0x8B232A08304282D8)}, + {0x3FFC, LIT64(0x92FB70B8D29AE2F9)}, + {0x3FFC, LIT64(0x9ACF476F5CCD1CB4)}, + {0x3FFC, LIT64(0xA29E76304954F23F)}, + {0x3FFC, LIT64(0xAA68C5D08AB85230)}, + {0x3FFC, LIT64(0xB22DFFFD9D539F83)}, + {0x3FFC, LIT64(0xB9EDEF453E900EA5)}, + {0x3FFC, LIT64(0xC1A85F1CC75E3EA5)}, + {0x3FFC, LIT64(0xC95D1BE828138DE6)}, + {0x3FFC, LIT64(0xD10BF300840D2DE4)}, + {0x3FFC, LIT64(0xD8B4B2BA6BC05E7A)}, + {0x3FFC, LIT64(0xE0572A6BB42335F6)}, + {0x3FFC, LIT64(0xE7F32A70EA9CAA8F)}, + {0x3FFC, LIT64(0xEF88843264ECEFAA)}, + {0x3FFC, LIT64(0xF7170A28ECC06666)}, + {0x3FFD, LIT64(0x812FD288332DAD32)}, + {0x3FFD, LIT64(0x88A8D1B1218E4D64)}, + {0x3FFD, LIT64(0x9012AB3F23E4AEE8)}, + {0x3FFD, LIT64(0x976CC3D411E7F1B9)}, + {0x3FFD, LIT64(0x9EB689493889A227)}, + {0x3FFD, LIT64(0xA5EF72C34487361B)}, + {0x3FFD, LIT64(0xAD1700BAF07A7227)}, + {0x3FFD, LIT64(0xB42CBCFAFD37EFB7)}, + {0x3FFD, LIT64(0xBB303A940BA80F89)}, + {0x3FFD, LIT64(0xC22115C6FCAEBBAF)}, + {0x3FFD, LIT64(0xC8FEF3E686331221)}, + {0x3FFD, LIT64(0xCFC98330B4000C70)}, + {0x3FFD, LIT64(0xD6807AA1102C5BF9)}, + {0x3FFD, LIT64(0xDD2399BC31252AA3)}, + {0x3FFD, LIT64(0xE3B2A8556B8FC517)}, + {0x3FFD, LIT64(0xEA2D764F64315989)}, + {0x3FFD, LIT64(0xF3BF5BF8BAD1A21D)}, + {0x3FFE, LIT64(0x801CE39E0D205C9A)}, + {0x3FFE, LIT64(0x8630A2DADA1ED066)}, + {0x3FFE, LIT64(0x8C1AD445F3E09B8C)}, + {0x3FFE, LIT64(0x91DB8F1664F350E2)}, + {0x3FFE, LIT64(0x97731420365E538C)}, + {0x3FFE, LIT64(0x9CE1C8E6A0B8CDBA)}, + {0x3FFE, LIT64(0xA22832DBCADAAE09)}, + {0x3FFE, LIT64(0xA746F2DDB7602294)}, + {0x3FFE, LIT64(0xAC3EC0FB997DD6A2)}, + {0x3FFE, LIT64(0xB110688AEBDC6F6A)}, + {0x3FFE, LIT64(0xB5BCC49059ECC4B0)}, + {0x3FFE, LIT64(0xBA44BC7DD470782F)}, + {0x3FFE, LIT64(0xBEA94144FD049AAC)}, + {0x3FFE, LIT64(0xC2EB4ABB661628B6)}, + {0x3FFE, LIT64(0xC70BD54CE602EE14)}, + {0x3FFE, LIT64(0xCD000549ADEC7159)}, + {0x3FFE, LIT64(0xD48457D2D8EA4EA3)}, + {0x3FFE, LIT64(0xDB948DA712DECE3B)}, + {0x3FFE, LIT64(0xE23855F969E8096A)}, + {0x3FFE, LIT64(0xE8771129C4353259)}, + {0x3FFE, LIT64(0xEE57C16E0D379C0D)}, + {0x3FFE, LIT64(0xF3E10211A87C3779)}, + {0x3FFE, LIT64(0xF919039D758B8D41)}, + {0x3FFE, LIT64(0xFE058B8F64935FB3)}, + {0x3FFF, LIT64(0x8155FB497B685D04)}, + {0x3FFF, LIT64(0x83889E3549D108E1)}, + {0x3FFF, LIT64(0x859CFA76511D724B)}, + {0x3FFF, LIT64(0x87952ECFFF8131E7)}, + {0x3FFF, LIT64(0x89732FD19557641B)}, + {0x3FFF, LIT64(0x8B38CAD101932A35)}, + {0x3FFF, LIT64(0x8CE7A8D8301EE6B5)}, + {0x3FFF, LIT64(0x8F46A39E2EAE5281)}, + {0x3FFF, LIT64(0x922DA7D791888487)}, + {0x3FFF, LIT64(0x94D19FCBDEDF5241)}, + {0x3FFF, LIT64(0x973AB94419D2A08B)}, + {0x3FFF, LIT64(0x996FF00E08E10B96)}, + {0x3FFF, LIT64(0x9B773F9512321DA7)}, + {0x3FFF, LIT64(0x9D55CC320F935624)}, + {0x3FFF, LIT64(0x9F100575006CC571)}, + {0x3FFF, LIT64(0xA0A9C290D97CC06C)}, + {0x3FFF, LIT64(0xA22659EBEBC0630A)}, + {0x3FFF, LIT64(0xA388B4AFF6EF0EC9)}, + {0x3FFF, LIT64(0xA4D35F1061D292C4)}, + {0x3FFF, LIT64(0xA60895DCFBE3187E)}, + {0x3FFF, LIT64(0xA72A51DC7367BEAC)}, + {0x3FFF, LIT64(0xA83A51530956168F)}, + {0x3FFF, LIT64(0xA93A20077539546E)}, + {0x3FFF, LIT64(0xAA9E7245023B2605)}, + {0x3FFF, LIT64(0xAC4C84BA6FE4D58F)}, + {0x3FFF, LIT64(0xADCE4A4A606B9712)}, + {0x3FFF, LIT64(0xAF2A2DCD8D263C9C)}, + {0x3FFF, LIT64(0xB0656F81F22265C7)}, + {0x3FFF, LIT64(0xB18465150F71496A)}, + {0x3FFF, LIT64(0xB28AAA156F9ADA35)}, + {0x3FFF, LIT64(0xB37B44FF3766B895)}, + {0x3FFF, LIT64(0xB458C3DCE9630433)}, + {0x3FFF, LIT64(0xB525529D562246BD)}, + {0x3FFF, LIT64(0xB5E2CCA95F9D88CC)}, + {0x3FFF, LIT64(0xB692CADA7ACA1ADA)}, + {0x3FFF, LIT64(0xB736AEA7A6925838)}, + {0x3FFF, LIT64(0xB7CFAB287E9F7B36)}, + {0x3FFF, LIT64(0xB85ECC66CB219835)}, + {0x3FFF, LIT64(0xB8E4FD5A20A593DA)}, + {0x3FFF, LIT64(0xB99F41F64AFF9BB5)}, + {0x3FFF, LIT64(0xBA7F1E17842BBE7B)}, + {0x3FFF, LIT64(0xBB4712857637E17D)}, + {0x3FFF, LIT64(0xBBFABE8A4788DF6F)}, + {0x3FFF, LIT64(0xBC9D0FAD2B689D79)}, + {0x3FFF, LIT64(0xBD306A39471ECD86)}, + {0x3FFF, LIT64(0xBDB6C731856AF18A)}, + {0x3FFF, LIT64(0xBE31CAC502E80D70)}, + {0x3FFF, LIT64(0xBEA2D55CE33194E2)}, + {0x3FFF, LIT64(0xBF0B10B7C03128F0)}, + {0x3FFF, LIT64(0xBF6B7A18DACB778D)}, + {0x3FFF, LIT64(0xBFC4EA4663FA18F6)}, + {0x3FFF, LIT64(0xC0181BDE8B89A454)}, + {0x3FFF, LIT64(0xC065B066CFBF6439)}, + {0x3FFF, LIT64(0xC0AE345F56340AE6)}, + {0x3FFF, LIT64(0xC0F222919CB9E6A7)} +}; + + +static const floatx80 exp_tbl[64] = { + {0x3FFF, LIT64(0x8000000000000000)}, + {0x3FFF, LIT64(0x8164D1F3BC030774)}, + {0x3FFF, LIT64(0x82CD8698AC2BA1D8)}, + {0x3FFF, LIT64(0x843A28C3ACDE4048)}, + {0x3FFF, LIT64(0x85AAC367CC487B14)}, + {0x3FFF, LIT64(0x871F61969E8D1010)}, + {0x3FFF, LIT64(0x88980E8092DA8528)}, + {0x3FFF, LIT64(0x8A14D575496EFD9C)}, + {0x3FFF, LIT64(0x8B95C1E3EA8BD6E8)}, + {0x3FFF, LIT64(0x8D1ADF5B7E5BA9E4)}, + {0x3FFF, LIT64(0x8EA4398B45CD53C0)}, + {0x3FFF, LIT64(0x9031DC431466B1DC)}, + {0x3FFF, LIT64(0x91C3D373AB11C338)}, + {0x3FFF, LIT64(0x935A2B2F13E6E92C)}, + {0x3FFF, LIT64(0x94F4EFA8FEF70960)}, + {0x3FFF, LIT64(0x96942D3720185A00)}, + {0x3FFF, LIT64(0x9837F0518DB8A970)}, + {0x3FFF, LIT64(0x99E0459320B7FA64)}, + {0x3FFF, LIT64(0x9B8D39B9D54E5538)}, + {0x3FFF, LIT64(0x9D3ED9A72CFFB750)}, + {0x3FFF, LIT64(0x9EF5326091A111AC)}, + {0x3FFF, LIT64(0xA0B0510FB9714FC4)}, + {0x3FFF, LIT64(0xA27043030C496818)}, + {0x3FFF, LIT64(0xA43515AE09E680A0)}, + {0x3FFF, LIT64(0xA5FED6A9B15138EC)}, + {0x3FFF, LIT64(0xA7CD93B4E9653568)}, + {0x3FFF, LIT64(0xA9A15AB4EA7C0EF8)}, + {0x3FFF, LIT64(0xAB7A39B5A93ED338)}, + {0x3FFF, LIT64(0xAD583EEA42A14AC8)}, + {0x3FFF, LIT64(0xAF3B78AD690A4374)}, + {0x3FFF, LIT64(0xB123F581D2AC2590)}, + {0x3FFF, LIT64(0xB311C412A9112488)}, + {0x3FFF, LIT64(0xB504F333F9DE6484)}, + {0x3FFF, LIT64(0xB6FD91E328D17790)}, + {0x3FFF, LIT64(0xB8FBAF4762FB9EE8)}, + {0x3FFF, LIT64(0xBAFF5AB2133E45FC)}, + {0x3FFF, LIT64(0xBD08A39F580C36C0)}, + {0x3FFF, LIT64(0xBF1799B67A731084)}, + {0x3FFF, LIT64(0xC12C4CCA66709458)}, + {0x3FFF, LIT64(0xC346CCDA24976408)}, + {0x3FFF, LIT64(0xC5672A115506DADC)}, + {0x3FFF, LIT64(0xC78D74C8ABB9B15C)}, + {0x3FFF, LIT64(0xC9B9BD866E2F27A4)}, + {0x3FFF, LIT64(0xCBEC14FEF2727C5C)}, + {0x3FFF, LIT64(0xCE248C151F8480E4)}, + {0x3FFF, LIT64(0xD06333DAEF2B2594)}, + {0x3FFF, LIT64(0xD2A81D91F12AE45C)}, + {0x3FFF, LIT64(0xD4F35AABCFEDFA20)}, + {0x3FFF, LIT64(0xD744FCCAD69D6AF4)}, + {0x3FFF, LIT64(0xD99D15C278AFD7B4)}, + {0x3FFF, LIT64(0xDBFBB797DAF23754)}, + {0x3FFF, LIT64(0xDE60F4825E0E9124)}, + {0x3FFF, LIT64(0xE0CCDEEC2A94E110)}, + {0x3FFF, LIT64(0xE33F8972BE8A5A50)}, + {0x3FFF, LIT64(0xE5B906E77C8348A8)}, + {0x3FFF, LIT64(0xE8396A503C4BDC68)}, + {0x3FFF, LIT64(0xEAC0C6E7DD243930)}, + {0x3FFF, LIT64(0xED4F301ED9942B84)}, + {0x3FFF, LIT64(0xEFE4B99BDCDAF5CC)}, + {0x3FFF, LIT64(0xF281773C59FFB138)}, + {0x3FFF, LIT64(0xF5257D152486CC2C)}, + {0x3FFF, LIT64(0xF7D0DF730AD13BB8)}, + {0x3FFF, LIT64(0xFA83B2DB722A033C)}, + {0x3FFF, LIT64(0xFD3E0C0CF486C174)} +}; + +static const float32 exp_tbl2[64] = { + 0x00000000, 0x9F841A9B, 0x9FC1D5B9, 0xA0728369, + 0x1FC5C95C, 0x1EE85C9F, 0x9FA20729, 0xA07BF9AF, + 0xA0020DCF, 0x205A63DA, 0x1EB70051, 0x1F6EB029, + 0xA0781494, 0x9EB319B0, 0x2017457D, 0x1F11D537, + 0x9FB952DD, 0x1FE43087, 0x1FA2A818, 0x1FDE494D, + 0x20504890, 0xA073691C, 0x1F9B7A05, 0xA0797126, + 0xA071A140, 0x204F62DA, 0x1F283C4A, 0x9F9A7FDC, + 0xA05B3FAC, 0x1FDF2610, 0x9F705F90, 0x201F678A, + 0x1F32FB13, 0x20038B30, 0x200DC3CC, 0x9F8B2AE6, + 0xA02BBF70, 0xA00BF518, 0xA041DD41, 0x9FDF137B, + 0x201F1568, 0x1FC13A2E, 0xA03F8F03, 0x1FF4907D, + 0x9E6E53E4, 0x1FD6D45C, 0xA076EDB9, 0x9FA6DE21, + 0x1EE69A2F, 0x207F439F, 0x201EC207, 0x9E8BE175, + 0x20032C4B, 0x2004DFF5, 0x1E72F47A, 0x1F722F22, + 0xA017E945, 0x1F401A5B, 0x9FB9A9E3, 0x20744C05, + 0x1F773A19, 0x1FFE90D5, 0xA041ED22, 0x1F853F3A +}; + + +static const floatx80 exp2_tbl[64] = { + {0x3FFF, LIT64(0x8000000000000000)}, + {0x3FFF, LIT64(0x8164D1F3BC030773)}, + {0x3FFF, LIT64(0x82CD8698AC2BA1D7)}, + {0x3FFF, LIT64(0x843A28C3ACDE4046)}, + {0x3FFF, LIT64(0x85AAC367CC487B15)}, + {0x3FFF, LIT64(0x871F61969E8D1010)}, + {0x3FFF, LIT64(0x88980E8092DA8527)}, + {0x3FFF, LIT64(0x8A14D575496EFD9A)}, + {0x3FFF, LIT64(0x8B95C1E3EA8BD6E7)}, + {0x3FFF, LIT64(0x8D1ADF5B7E5BA9E6)}, + {0x3FFF, LIT64(0x8EA4398B45CD53C0)}, + {0x3FFF, LIT64(0x9031DC431466B1DC)}, + {0x3FFF, LIT64(0x91C3D373AB11C336)}, + {0x3FFF, LIT64(0x935A2B2F13E6E92C)}, + {0x3FFF, LIT64(0x94F4EFA8FEF70961)}, + {0x3FFF, LIT64(0x96942D3720185A00)}, + {0x3FFF, LIT64(0x9837F0518DB8A96F)}, + {0x3FFF, LIT64(0x99E0459320B7FA65)}, + {0x3FFF, LIT64(0x9B8D39B9D54E5539)}, + {0x3FFF, LIT64(0x9D3ED9A72CFFB751)}, + {0x3FFF, LIT64(0x9EF5326091A111AE)}, + {0x3FFF, LIT64(0xA0B0510FB9714FC2)}, + {0x3FFF, LIT64(0xA27043030C496819)}, + {0x3FFF, LIT64(0xA43515AE09E6809E)}, + {0x3FFF, LIT64(0xA5FED6A9B15138EA)}, + {0x3FFF, LIT64(0xA7CD93B4E965356A)}, + {0x3FFF, LIT64(0xA9A15AB4EA7C0EF8)}, + {0x3FFF, LIT64(0xAB7A39B5A93ED337)}, + {0x3FFF, LIT64(0xAD583EEA42A14AC6)}, + {0x3FFF, LIT64(0xAF3B78AD690A4375)}, + {0x3FFF, LIT64(0xB123F581D2AC2590)}, + {0x3FFF, LIT64(0xB311C412A9112489)}, + {0x3FFF, LIT64(0xB504F333F9DE6484)}, + {0x3FFF, LIT64(0xB6FD91E328D17791)}, + {0x3FFF, LIT64(0xB8FBAF4762FB9EE9)}, + {0x3FFF, LIT64(0xBAFF5AB2133E45FB)}, + {0x3FFF, LIT64(0xBD08A39F580C36BF)}, + {0x3FFF, LIT64(0xBF1799B67A731083)}, + {0x3FFF, LIT64(0xC12C4CCA66709456)}, + {0x3FFF, LIT64(0xC346CCDA24976407)}, + {0x3FFF, LIT64(0xC5672A115506DADD)}, + {0x3FFF, LIT64(0xC78D74C8ABB9B15D)}, + {0x3FFF, LIT64(0xC9B9BD866E2F27A3)}, + {0x3FFF, LIT64(0xCBEC14FEF2727C5D)}, + {0x3FFF, LIT64(0xCE248C151F8480E4)}, + {0x3FFF, LIT64(0xD06333DAEF2B2595)}, + {0x3FFF, LIT64(0xD2A81D91F12AE45A)}, + {0x3FFF, LIT64(0xD4F35AABCFEDFA1F)}, + {0x3FFF, LIT64(0xD744FCCAD69D6AF4)}, + {0x3FFF, LIT64(0xD99D15C278AFD7B6)}, + {0x3FFF, LIT64(0xDBFBB797DAF23755)}, + {0x3FFF, LIT64(0xDE60F4825E0E9124)}, + {0x3FFF, LIT64(0xE0CCDEEC2A94E111)}, + {0x3FFF, LIT64(0xE33F8972BE8A5A51)}, + {0x3FFF, LIT64(0xE5B906E77C8348A8)}, + {0x3FFF, LIT64(0xE8396A503C4BDC68)}, + {0x3FFF, LIT64(0xEAC0C6E7DD24392F)}, + {0x3FFF, LIT64(0xED4F301ED9942B84)}, + {0x3FFF, LIT64(0xEFE4B99BDCDAF5CB)}, + {0x3FFF, LIT64(0xF281773C59FFB13A)}, + {0x3FFF, LIT64(0xF5257D152486CC2C)}, + {0x3FFF, LIT64(0xF7D0DF730AD13BB9)}, + {0x3FFF, LIT64(0xFA83B2DB722A033A)}, + {0x3FFF, LIT64(0xFD3E0C0CF486C175)} +}; + + +static const float32 exp2_tbl2[64] = { + 0x3F738000, 0x3FBEF7CA, 0x3FBDF8A9, 0x3FBCD7C9, + 0xBFBDE8DA, 0x3FBDE85C, 0x3FBEBBF1, 0x3FBB80CA, + 0xBFBA8373, 0xBFBE9670, 0x3FBDB700, 0x3FBEEEB0, + 0x3FBBFD6D, 0xBFBDB319, 0x3FBDBA2B, 0x3FBE91D5, + 0x3FBE8D5A, 0xBFBCDE7B, 0xBFBEBAAF, 0xBFBD86DA, + 0xBFBEBEDD, 0x3FBCC96E, 0xBFBEC90B, 0x3FBBD1DB, + 0x3FBCE5EB, 0xBFBEC274, 0x3FBEA83C, 0x3FBECB00, + 0x3FBE9301, 0xBFBD8367, 0xBFBEF05F, 0x3FBDFB3C, + 0x3FBEB2FB, 0x3FBAE2CB, 0x3FBCDC3C, 0x3FBEE9AA, + 0xBFBEAEFD, 0xBFBCBF51, 0x3FBEF88A, 0x3FBD83B2, + 0x3FBDF8AB, 0xBFBDFB17, 0xBFBEFE3C, 0xBFBBB6F8, + 0xBFBCEE53, 0xBFBDA4AE, 0x3FBC9124, 0x3FBEB243, + 0x3FBDE69A, 0xBFB8BC61, 0x3FBDF610, 0xBFBD8BE1, + 0x3FBACB12, 0x3FBB9BFE, 0x3FBCF2F4, 0x3FBEF22F, + 0xBFBDBF4A, 0x3FBEC01A, 0x3FBE8CAC, 0xBFBCBB3F, + 0x3FBEF73A, 0xBFB8B795, 0x3FBEF84B, 0xBFBEF581 +}; + + +static const floatx80 log_tbl[128] = { + {0x3FFE, LIT64(0xFE03F80FE03F80FE)}, + {0x3FF7, LIT64(0xFF015358833C47E2)}, + {0x3FFE, LIT64(0xFA232CF252138AC0)}, + {0x3FF9, LIT64(0xBDC8D83EAD88D549)}, + {0x3FFE, LIT64(0xF6603D980F6603DA)}, + {0x3FFA, LIT64(0x9CF43DCFF5EAFD48)}, + {0x3FFE, LIT64(0xF2B9D6480F2B9D65)}, + {0x3FFA, LIT64(0xDA16EB88CB8DF614)}, + {0x3FFE, LIT64(0xEF2EB71FC4345238)}, + {0x3FFB, LIT64(0x8B29B7751BD70743)}, + {0x3FFE, LIT64(0xEBBDB2A5C1619C8C)}, + {0x3FFB, LIT64(0xA8D839F830C1FB49)}, + {0x3FFE, LIT64(0xE865AC7B7603A197)}, + {0x3FFB, LIT64(0xC61A2EB18CD907AD)}, + {0x3FFE, LIT64(0xE525982AF70C880E)}, + {0x3FFB, LIT64(0xE2F2A47ADE3A18AF)}, + {0x3FFE, LIT64(0xE1FC780E1FC780E2)}, + {0x3FFB, LIT64(0xFF64898EDF55D551)}, + {0x3FFE, LIT64(0xDEE95C4CA037BA57)}, + {0x3FFC, LIT64(0x8DB956A97B3D0148)}, + {0x3FFE, LIT64(0xDBEB61EED19C5958)}, + {0x3FFC, LIT64(0x9B8FE100F47BA1DE)}, + {0x3FFE, LIT64(0xD901B2036406C80E)}, + {0x3FFC, LIT64(0xA9372F1D0DA1BD17)}, + {0x3FFE, LIT64(0xD62B80D62B80D62C)}, + {0x3FFC, LIT64(0xB6B07F38CE90E46B)}, + {0x3FFE, LIT64(0xD3680D3680D3680D)}, + {0x3FFC, LIT64(0xC3FD032906488481)}, + {0x3FFE, LIT64(0xD0B69FCBD2580D0B)}, + {0x3FFC, LIT64(0xD11DE0FF15AB18CA)}, + {0x3FFE, LIT64(0xCE168A7725080CE1)}, + {0x3FFC, LIT64(0xDE1433A16C66B150)}, + {0x3FFE, LIT64(0xCB8727C065C393E0)}, + {0x3FFC, LIT64(0xEAE10B5A7DDC8ADD)}, + {0x3FFE, LIT64(0xC907DA4E871146AD)}, + {0x3FFC, LIT64(0xF7856E5EE2C9B291)}, + {0x3FFE, LIT64(0xC6980C6980C6980C)}, + {0x3FFD, LIT64(0x82012CA5A68206D7)}, + {0x3FFE, LIT64(0xC4372F855D824CA6)}, + {0x3FFD, LIT64(0x882C5FCD7256A8C5)}, + {0x3FFE, LIT64(0xC1E4BBD595F6E947)}, + {0x3FFD, LIT64(0x8E44C60B4CCFD7DE)}, + {0x3FFE, LIT64(0xBFA02FE80BFA02FF)}, + {0x3FFD, LIT64(0x944AD09EF4351AF6)}, + {0x3FFE, LIT64(0xBD69104707661AA3)}, + {0x3FFD, LIT64(0x9A3EECD4C3EAA6B2)}, + {0x3FFE, LIT64(0xBB3EE721A54D880C)}, + {0x3FFD, LIT64(0xA0218434353F1DE8)}, + {0x3FFE, LIT64(0xB92143FA36F5E02E)}, + {0x3FFD, LIT64(0xA5F2FCABBBC506DA)}, + {0x3FFE, LIT64(0xB70FBB5A19BE3659)}, + {0x3FFD, LIT64(0xABB3B8BA2AD362A5)}, + {0x3FFE, LIT64(0xB509E68A9B94821F)}, + {0x3FFD, LIT64(0xB1641795CE3CA97B)}, + {0x3FFE, LIT64(0xB30F63528917C80B)}, + {0x3FFD, LIT64(0xB70475515D0F1C61)}, + {0x3FFE, LIT64(0xB11FD3B80B11FD3C)}, + {0x3FFD, LIT64(0xBC952AFEEA3D13E1)}, + {0x3FFE, LIT64(0xAF3ADDC680AF3ADE)}, + {0x3FFD, LIT64(0xC2168ED0F458BA4A)}, + {0x3FFE, LIT64(0xAD602B580AD602B6)}, + {0x3FFD, LIT64(0xC788F439B3163BF1)}, + {0x3FFE, LIT64(0xAB8F69E28359CD11)}, + {0x3FFD, LIT64(0xCCECAC08BF04565D)}, + {0x3FFE, LIT64(0xA9C84A47A07F5638)}, + {0x3FFD, LIT64(0xD24204872DD85160)}, + {0x3FFE, LIT64(0xA80A80A80A80A80B)}, + {0x3FFD, LIT64(0xD78949923BC3588A)}, + {0x3FFE, LIT64(0xA655C4392D7B73A8)}, + {0x3FFD, LIT64(0xDCC2C4B49887DACC)}, + {0x3FFE, LIT64(0xA4A9CF1D96833751)}, + {0x3FFD, LIT64(0xE1EEBD3E6D6A6B9E)}, + {0x3FFE, LIT64(0xA3065E3FAE7CD0E0)}, + {0x3FFD, LIT64(0xE70D785C2F9F5BDC)}, + {0x3FFE, LIT64(0xA16B312EA8FC377D)}, + {0x3FFD, LIT64(0xEC1F392C5179F283)}, + {0x3FFE, LIT64(0x9FD809FD809FD80A)}, + {0x3FFD, LIT64(0xF12440D3E36130E6)}, + {0x3FFE, LIT64(0x9E4CAD23DD5F3A20)}, + {0x3FFD, LIT64(0xF61CCE92346600BB)}, + {0x3FFE, LIT64(0x9CC8E160C3FB19B9)}, + {0x3FFD, LIT64(0xFB091FD38145630A)}, + {0x3FFE, LIT64(0x9B4C6F9EF03A3CAA)}, + {0x3FFD, LIT64(0xFFE97042BFA4C2AD)}, + {0x3FFE, LIT64(0x99D722DABDE58F06)}, + {0x3FFE, LIT64(0x825EFCED49369330)}, + {0x3FFE, LIT64(0x9868C809868C8098)}, + {0x3FFE, LIT64(0x84C37A7AB9A905C9)}, + {0x3FFE, LIT64(0x97012E025C04B809)}, + {0x3FFE, LIT64(0x87224C2E8E645FB7)}, + {0x3FFE, LIT64(0x95A02568095A0257)}, + {0x3FFE, LIT64(0x897B8CAC9F7DE298)}, + {0x3FFE, LIT64(0x9445809445809446)}, + {0x3FFE, LIT64(0x8BCF55DEC4CD05FE)}, + {0x3FFE, LIT64(0x92F113840497889C)}, + {0x3FFE, LIT64(0x8E1DC0FB89E125E5)}, + {0x3FFE, LIT64(0x91A2B3C4D5E6F809)}, + {0x3FFE, LIT64(0x9066E68C955B6C9B)}, + {0x3FFE, LIT64(0x905A38633E06C43B)}, + {0x3FFE, LIT64(0x92AADE74C7BE59E0)}, + {0x3FFE, LIT64(0x8F1779D9FDC3A219)}, + {0x3FFE, LIT64(0x94E9BFF615845643)}, + {0x3FFE, LIT64(0x8DDA520237694809)}, + {0x3FFE, LIT64(0x9723A1B720134203)}, + {0x3FFE, LIT64(0x8CA29C046514E023)}, + {0x3FFE, LIT64(0x995899C890EB8990)}, + {0x3FFE, LIT64(0x8B70344A139BC75A)}, + {0x3FFE, LIT64(0x9B88BDAA3A3DAE2F)}, + {0x3FFE, LIT64(0x8A42F8705669DB46)}, + {0x3FFE, LIT64(0x9DB4224FFFE1157C)}, + {0x3FFE, LIT64(0x891AC73AE9819B50)}, + {0x3FFE, LIT64(0x9FDADC268B7A12DA)}, + {0x3FFE, LIT64(0x87F78087F78087F8)}, + {0x3FFE, LIT64(0xA1FCFF17CE733BD4)}, + {0x3FFE, LIT64(0x86D905447A34ACC6)}, + {0x3FFE, LIT64(0xA41A9E8F5446FB9F)}, + {0x3FFE, LIT64(0x85BF37612CEE3C9B)}, + {0x3FFE, LIT64(0xA633CD7E6771CD8B)}, + {0x3FFE, LIT64(0x84A9F9C8084A9F9D)}, + {0x3FFE, LIT64(0xA8489E600B435A5E)}, + {0x3FFE, LIT64(0x839930523FBE3368)}, + {0x3FFE, LIT64(0xAA59233CCCA4BD49)}, + {0x3FFE, LIT64(0x828CBFBEB9A020A3)}, + {0x3FFE, LIT64(0xAC656DAE6BCC4985)}, + {0x3FFE, LIT64(0x81848DA8FAF0D277)}, + {0x3FFE, LIT64(0xAE6D8EE360BB2468)}, + {0x3FFE, LIT64(0x8080808080808081)}, + {0x3FFE, LIT64(0xB07197A23C46C654)} +}; + + +static const floatx80 pi_tbl[65] = { + {0xC004, LIT64(0xC90FDAA22168C235)}, + {0xC004, LIT64(0xC2C75BCD105D7C23)}, + {0xC004, LIT64(0xBC7EDCF7FF523611)}, + {0xC004, LIT64(0xB6365E22EE46F000)}, + {0xC004, LIT64(0xAFEDDF4DDD3BA9EE)}, + {0xC004, LIT64(0xA9A56078CC3063DD)}, + {0xC004, LIT64(0xA35CE1A3BB251DCB)}, + {0xC004, LIT64(0x9D1462CEAA19D7B9)}, + {0xC004, LIT64(0x96CBE3F9990E91A8)}, + {0xC004, LIT64(0x9083652488034B96)}, + {0xC004, LIT64(0x8A3AE64F76F80584)}, + {0xC004, LIT64(0x83F2677A65ECBF73)}, + {0xC003, LIT64(0xFB53D14AA9C2F2C2)}, + {0xC003, LIT64(0xEEC2D3A087AC669F)}, + {0xC003, LIT64(0xE231D5F66595DA7B)}, + {0xC003, LIT64(0xD5A0D84C437F4E58)}, + {0xC003, LIT64(0xC90FDAA22168C235)}, + {0xC003, LIT64(0xBC7EDCF7FF523611)}, + {0xC003, LIT64(0xAFEDDF4DDD3BA9EE)}, + {0xC003, LIT64(0xA35CE1A3BB251DCB)}, + {0xC003, LIT64(0x96CBE3F9990E91A8)}, + {0xC003, LIT64(0x8A3AE64F76F80584)}, + {0xC002, LIT64(0xFB53D14AA9C2F2C2)}, + {0xC002, LIT64(0xE231D5F66595DA7B)}, + {0xC002, LIT64(0xC90FDAA22168C235)}, + {0xC002, LIT64(0xAFEDDF4DDD3BA9EE)}, + {0xC002, LIT64(0x96CBE3F9990E91A8)}, + {0xC001, LIT64(0xFB53D14AA9C2F2C2)}, + {0xC001, LIT64(0xC90FDAA22168C235)}, + {0xC001, LIT64(0x96CBE3F9990E91A8)}, + {0xC000, LIT64(0xC90FDAA22168C235)}, + {0xBFFF, LIT64(0xC90FDAA22168C235)}, + {0x0000, LIT64(0x0000000000000000)}, + {0x3FFF, LIT64(0xC90FDAA22168C235)}, + {0x4000, LIT64(0xC90FDAA22168C235)}, + {0x4001, LIT64(0x96CBE3F9990E91A8)}, + {0x4001, LIT64(0xC90FDAA22168C235)}, + {0x4001, LIT64(0xFB53D14AA9C2F2C2)}, + {0x4002, LIT64(0x96CBE3F9990E91A8)}, + {0x4002, LIT64(0xAFEDDF4DDD3BA9EE)}, + {0x4002, LIT64(0xC90FDAA22168C235)}, + {0x4002, LIT64(0xE231D5F66595DA7B)}, + {0x4002, LIT64(0xFB53D14AA9C2F2C2)}, + {0x4003, LIT64(0x8A3AE64F76F80584)}, + {0x4003, LIT64(0x96CBE3F9990E91A8)}, + {0x4003, LIT64(0xA35CE1A3BB251DCB)}, + {0x4003, LIT64(0xAFEDDF4DDD3BA9EE)}, + {0x4003, LIT64(0xBC7EDCF7FF523611)}, + {0x4003, LIT64(0xC90FDAA22168C235)}, + {0x4003, LIT64(0xD5A0D84C437F4E58)}, + {0x4003, LIT64(0xE231D5F66595DA7B)}, + {0x4003, LIT64(0xEEC2D3A087AC669F)}, + {0x4003, LIT64(0xFB53D14AA9C2F2C2)}, + {0x4004, LIT64(0x83F2677A65ECBF73)}, + {0x4004, LIT64(0x8A3AE64F76F80584)}, + {0x4004, LIT64(0x9083652488034B96)}, + {0x4004, LIT64(0x96CBE3F9990E91A8)}, + {0x4004, LIT64(0x9D1462CEAA19D7B9)}, + {0x4004, LIT64(0xA35CE1A3BB251DCB)}, + {0x4004, LIT64(0xA9A56078CC3063DD)}, + {0x4004, LIT64(0xAFEDDF4DDD3BA9EE)}, + {0x4004, LIT64(0xB6365E22EE46F000)}, + {0x4004, LIT64(0xBC7EDCF7FF523611)}, + {0x4004, LIT64(0xC2C75BCD105D7C23)}, + {0x4004, LIT64(0xC90FDAA22168C235)} +}; + + +static const float32 pi_tbl2[65] = { + 0x21800000, 0xA0D00000, 0xA1E80000, 0x21480000, + 0xA1200000, 0x21FC0000, 0x21100000, 0xA1580000, + 0x21E00000, 0x20B00000, 0xA1880000, 0x21C40000, + 0x20000000, 0x21380000, 0xA1300000, 0x9FC00000, + 0x21000000, 0xA1680000, 0xA0A00000, 0x20900000, + 0x21600000, 0xA1080000, 0x1F800000, 0xA0B00000, + 0x20800000, 0xA0200000, 0x20E00000, 0x1F000000, + 0x20000000, 0x20600000, 0x1F800000, 0x1F000000, + 0x00000000, + 0x9F000000, 0x9F800000, 0xA0600000, 0xA0000000, + 0x9F000000, 0xA0E00000, 0x20200000, 0xA0800000, + 0x20B00000, 0x9F800000, 0x21080000, 0xA1600000, + 0xA0900000, 0x20A00000, 0x21680000, 0xA1000000, + 0x1FC00000, 0x21300000, 0xA1380000, 0xA0000000, + 0xA1C40000, 0x21880000, 0xA0B00000, 0xA1E00000, + 0x21580000, 0xA1100000, 0xA1FC0000, 0x21200000, + 0xA1480000, 0x21E80000, 0x20D00000, 0xA1800000 +};