float FN_PROTOTYPE(coshf)(float fx) { /* After dealing with special cases the computation is split into regions as follows: abs(x) >= max_cosh_arg: cosh(x) = sign(x)*Inf abs(x) >= small_threshold: cosh(x) = sign(x)*exp(abs(x))/2 computed using the splitexp and scaleDouble functions as for exp_amd(). abs(x) < small_threshold: compute p = exp(y) - 1 and then z = 0.5*(p+(p/(p+1.0))) cosh(x) is then sign(x)*z. */ static const double /* The max argument of coshf, but stored as a double */ max_cosh_arg = 8.94159862922329438106e+01, /* 0x40565a9f84f82e63 */ thirtytwo_by_log2 = 4.61662413084468283841e+01, /* 0x40471547652b82fe */ log2_by_32_lead = 2.16608493356034159660e-02, /* 0x3f962e42fe000000 */ log2_by_32_tail = 5.68948749532545630390e-11, /* 0x3dcf473de6af278e */ // small_threshold = 8*BASEDIGITS_DP64*0.30102999566398119521373889; small_threshold = 20.0; /* (8*BASEDIGITS_DP64*log10of2) ' exp(-x) insignificant c.f. exp(x) */ /* Tabulated values of sinh(i) and cosh(i) for i = 0,...,36. */ static const double sinh_lead[ 37] = { 0.00000000000000000000e+00, /* 0x0000000000000000 */ 1.17520119364380137839e+00, /* 0x3ff2cd9fc44eb982 */ 3.62686040784701857476e+00, /* 0x400d03cf63b6e19f */ 1.00178749274099008204e+01, /* 0x40240926e70949ad */ 2.72899171971277496596e+01, /* 0x403b4a3803703630 */ 7.42032105777887522891e+01, /* 0x40528d0166f07374 */ 2.01713157370279219549e+02, /* 0x406936d22f67c805 */ 5.48316123273246489589e+02, /* 0x408122876ba380c9 */ 1.49047882578955000099e+03, /* 0x409749ea514eca65 */ 4.05154190208278987484e+03, /* 0x40afa7157430966f */ 1.10132328747033916443e+04, /* 0x40c5829dced69991 */ 2.99370708492480553105e+04, /* 0x40dd3c4488cb48d6 */ 8.13773957064298447222e+04, /* 0x40f3de1654d043f0 */ 2.21206696003330085659e+05, /* 0x410b00b5916a31a5 */ 6.01302142081972560845e+05, /* 0x412259ac48bef7e3 */ 1.63450868623590236530e+06, /* 0x4138f0ccafad27f6 */ 4.44305526025387924165e+06, /* 0x4150f2ebd0a7ffe3 */ 1.20774763767876271158e+07, /* 0x416709348c0ea4ed */ 3.28299845686652474105e+07, /* 0x417f4f22091940bb */ 8.92411504815936237574e+07, /* 0x419546d8f9ed26e1 */ 2.42582597704895108938e+08, /* 0x41aceb088b68e803 */ 6.59407867241607308388e+08, /* 0x41c3a6e1fd9eecfd */ 1.79245642306579566002e+09, /* 0x41dab5adb9c435ff */ 4.87240172312445068359e+09, /* 0x41f226af33b1fdc0 */ 1.32445610649217357635e+10, /* 0x4208ab7fb5475fb7 */ 3.60024496686929321289e+10, /* 0x4220c3d3920962c8 */ 9.78648047144193725586e+10, /* 0x4236c932696a6b5c */ 2.66024120300899291992e+11, /* 0x424ef822f7f6731c */ 7.23128532145737548828e+11, /* 0x42650bba3796379a */ 1.96566714857202099609e+12, /* 0x427c9aae4631c056 */ 5.34323729076223046875e+12, /* 0x429370470aec28ec */ 1.45244248326237109375e+13, /* 0x42aa6b765d8cdf6c */ 3.94814800913403437500e+13, /* 0x42c1f43fcc4b662c */ 1.07321789892958031250e+14, /* 0x42d866f34a725782 */ 2.91730871263727437500e+14, /* 0x42f0953e2f3a1ef7 */ 7.93006726156715250000e+14, /* 0x430689e221bc8d5a */ 2.15561577355759750000e+15}; /* 0x431ea215a1d20d76 */ static const double cosh_lead[ 37] = { 1.00000000000000000000e+00, /* 0x3ff0000000000000 */ 1.54308063481524371241e+00, /* 0x3ff8b07551d9f550 */ 3.76219569108363138810e+00, /* 0x400e18fa0df2d9bc */ 1.00676619957777653269e+01, /* 0x402422a497d6185e */ 2.73082328360164865444e+01, /* 0x403b4ee858de3e80 */ 7.42099485247878334349e+01, /* 0x40528d6fcbeff3a9 */ 2.01715636122455890700e+02, /* 0x406936e67db9b919 */ 5.48317035155212010977e+02, /* 0x4081228949ba3a8b */ 1.49047916125217807348e+03, /* 0x409749eaa93f4e76 */ 4.05154202549259389343e+03, /* 0x40afa715845d8894 */ 1.10132329201033226127e+04, /* 0x40c5829dd053712d */ 2.99370708659497577173e+04, /* 0x40dd3c4489115627 */ 8.13773957125740562333e+04, /* 0x40f3de1654d6b543 */ 2.21206696005590405548e+05, /* 0x410b00b5916b6105 */ 6.01302142082804115489e+05, /* 0x412259ac48bf13ca */ 1.63450868623620807193e+06, /* 0x4138f0ccafad2d17 */ 4.44305526025399193168e+06, /* 0x4150f2ebd0a8005c */ 1.20774763767876680940e+07, /* 0x416709348c0ea503 */ 3.28299845686652623117e+07, /* 0x417f4f22091940bf */ 8.92411504815936237574e+07, /* 0x419546d8f9ed26e1 */ 2.42582597704895138741e+08, /* 0x41aceb088b68e804 */ 6.59407867241607308388e+08, /* 0x41c3a6e1fd9eecfd */ 1.79245642306579566002e+09, /* 0x41dab5adb9c435ff */ 4.87240172312445068359e+09, /* 0x41f226af33b1fdc0 */ 1.32445610649217357635e+10, /* 0x4208ab7fb5475fb7 */ 3.60024496686929321289e+10, /* 0x4220c3d3920962c8 */ 9.78648047144193725586e+10, /* 0x4236c932696a6b5c */ 2.66024120300899291992e+11, /* 0x424ef822f7f6731c */ 7.23128532145737548828e+11, /* 0x42650bba3796379a */ 1.96566714857202099609e+12, /* 0x427c9aae4631c056 */ 5.34323729076223046875e+12, /* 0x429370470aec28ec */ 1.45244248326237109375e+13, /* 0x42aa6b765d8cdf6c */ 3.94814800913403437500e+13, /* 0x42c1f43fcc4b662c */ 1.07321789892958031250e+14, /* 0x42d866f34a725782 */ 2.91730871263727437500e+14, /* 0x42f0953e2f3a1ef7 */ 7.93006726156715250000e+14, /* 0x430689e221bc8d5a */ 2.15561577355759750000e+15}; /* 0x431ea215a1d20d76 */ unsigned long ux, aux, xneg; double x = fx, y, z, z1, z2; int m; /* Special cases */ GET_BITS_DP64(x, ux); aux = ux & ~SIGNBIT_DP64; if (aux < 0x3e30000000000000) /* |x| small enough that cosh(x) = 1 */ { if (aux == 0) return (float)1.0; /* with no inexact */ if (LAMBDA_DP64 + x > 1.0) return valf_with_flags((float)1.0, AMD_F_INEXACT); /* with inexact */ } else if (aux >= PINFBITPATT_DP64) /* |x| is NaN or Inf */ { if (aux > PINFBITPATT_DP64) /* |x| is a NaN? */ return fx + fx; else /* x is infinity */ return infinityf_with_flags(0); } xneg = (aux != ux); y = x; if (xneg) y = -x; if (y >= max_cosh_arg) { /* Return infinity with overflow flag. */ #if 0 /* This way handles non-POSIX behaviour but weirdly causes sinhf to run half as fast for all arguments on Hammer */ return retval_errno_erange(fx, xneg); #else /* This handles POSIX behaviour */ __set_errno(ERANGE); z = infinityf_with_flags(AMD_F_OVERFLOW); #endif } else if (y >= small_threshold) { /* In this range y is large enough so that the negative exponential is negligible, so cosh(y) is approximated by sign(x)*exp(y)/2. The code below is an inlined version of that from exp() with two changes (it operates on y instead of x, and the division by 2 is done by reducing m by 1). */ splitexp(y, 1.0, thirtytwo_by_log2, log2_by_32_lead, log2_by_32_tail, &m, &z1, &z2); m -= 1; if (m >= EMIN_DP64 && m <= EMAX_DP64) z = scaleDouble_1((z1+z2),m); else z = scaleDouble_2((z1+z2),m); } else { /* In this range we find the integer part y0 of y and the increment dy = y - y0. We then compute z = sinh(y) = sinh(y0)cosh(dy) + cosh(y0)sinh(dy) z = cosh(y) = cosh(y0)cosh(dy) + sinh(y0)sinh(dy) where sinh(y0) and cosh(y0) are tabulated above. */ int ind; double dy, dy2, sdy, cdy; ind = (int)y; dy = y - ind; dy2 = dy*dy; sdy = dy + dy*dy2*(0.166666666666666667013899e0 + (0.833333333333329931873097e-2 + (0.198412698413242405162014e-3 + (0.275573191913636406057211e-5 + (0.250521176994133472333666e-7 + (0.160576793121939886190847e-9 + 0.7746188980094184251527126e-12*dy2)*dy2)*dy2)*dy2)*dy2)*dy2); cdy = 1 + dy2*(0.500000000000000005911074e0 + (0.416666666666660876512776e-1 + (0.138888888889814854814536e-2 + (0.248015872460622433115785e-4 + (0.275573350756016588011357e-6 + (0.208744349831471353536305e-8 + 0.1163921388172173692062032e-10*dy2)*dy2)*dy2)*dy2)*dy2)*dy2); z = cosh_lead[ind]*cdy + sinh_lead[ind]*sdy; } // if (xneg) z = - z; return (float)z; }
float FN_PROTOTYPE(atanhf)(float x) { double dx; unsigned int ux, ax; double r, t, poly; GET_BITS_SP32(x, ux); ax = ux & ~SIGNBIT_SP32; if ((ux & EXPBITS_SP32) == EXPBITS_SP32) { /* x is either NaN or infinity */ if (ux & MANTBITS_SP32) { /* x is NaN */ #ifdef WINDOWS return handle_errorf(_FUNCNAME, ux|0x00400000, _DOMAIN, 0, EDOM, x, 0.0F); #else return x + x; /* Raise invalid if it is a signalling NaN */ #endif } else { /* x is infinity; return a NaN */ #ifdef WINDOWS return handle_errorf(_FUNCNAME, INDEFBITPATT_SP32, _DOMAIN, AMD_F_INVALID, EDOM, x, 0.0F); #else return retval_errno_edom(x,nanf_with_flags(AMD_F_INVALID)); #endif } } else if (ax >= 0x3f800000) { if (ax > 0x3f800000) { /* abs(x) > 1.0; return NaN */ #ifdef WINDOWS return handle_errorf(_FUNCNAME, INDEFBITPATT_SP32, _DOMAIN, AMD_F_INVALID, EDOM, x, 0.0F); #else return retval_errno_edom(x,nanf_with_flags(AMD_F_INVALID)); #endif } else if (ux == 0x3f800000) { /* x = +1.0; return infinity with the same sign as x and set the divbyzero status flag */ #ifdef WINDOWS return handle_errorf(_FUNCNAME, PINFBITPATT_SP32, _DOMAIN, AMD_F_INVALID, EDOM, x, 0.0F); #else return retval_errno_edom(x,infinityf_with_flags(AMD_F_DIVBYZERO)); #endif } else { /* x = -1.0; return infinity with the same sign as x */ #ifdef WINDOWS return handle_errorf(_FUNCNAME, NINFBITPATT_SP32, _DOMAIN, AMD_F_INVALID, EDOM, x, 0.0F); #else return retval_errno_edom(x,-infinityf_with_flags(AMD_F_DIVBYZERO)); #endif } } if (ax < 0x39000000) { if (ax == 0x00000000) { /* x is +/-zero. Return the same zero. */ return x; } else { /* Arguments smaller than 2^(-13) in magnitude are approximated by atanhf(x) = x, raising inexact flag. */ return valf_with_flags(x, AMD_F_INEXACT); } } else { dx = x; if (ax < 0x3f000000) { /* Arguments up to 0.5 in magnitude are approximated by a [2,2] minimax polynomial */ t = dx*dx; poly = (0.39453629046e0 + (-0.28120347286e0 + 0.92834212715e-2 * t) * t) / (0.11836088638e1 + (-0.15537744551e1 + 0.45281890445e0 * t) * t); return (float)(dx + dx*t*poly); } else { /* abs(x) >= 0.5 */ /* Note that atanhf(x) = 0.5 * ln((1+x)/(1-x)) (see Abramowitz and Stegun 4.6.22). For greater accuracy we use the variant formula atanhf(x) = log(1 + 2x/(1-x)) = log1p(2x/(1-x)). */ if (ux & SIGNBIT_SP32) { /* Argument x is negative */ r = (-2.0 * dx) / (1.0 + dx); r = 0.5 * FN_PROTOTYPE(log1p)(r); return (float)-r; } else { r = (2.0 * dx) / (1.0 - dx); r = 0.5 * FN_PROTOTYPE(log1p)(r); return (float)r; } } } }