Elliott Hughes | c729d4f | 2014-09-12 16:09:40 -0700 | [diff] [blame] | 1 | /* from: FreeBSD: head/lib/msun/src/s_tanhl.c XXX */ |
| 2 | |
| 3 | /* @(#)s_tanh.c 5.1 93/09/24 */ |
| 4 | /* |
| 5 | * ==================================================== |
| 6 | * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. |
| 7 | * |
| 8 | * Developed at SunPro, a Sun Microsystems, Inc. business. |
| 9 | * Permission to use, copy, modify, and distribute this |
| 10 | * software is freely granted, provided that this notice |
| 11 | * is preserved. |
| 12 | * ==================================================== |
| 13 | */ |
| 14 | |
| 15 | #include <sys/cdefs.h> |
Elliott Hughes | bac0ebb | 2021-01-26 14:17:20 -0800 | [diff] [blame] | 16 | __FBSDID("$FreeBSD$"); |
Elliott Hughes | c729d4f | 2014-09-12 16:09:40 -0700 | [diff] [blame] | 17 | |
| 18 | /* |
| 19 | * See s_tanh.c for complete comments. |
| 20 | * |
| 21 | * Converted to long double by Bruce D. Evans. |
| 22 | */ |
| 23 | |
| 24 | #include <float.h> |
| 25 | #ifdef __i386__ |
| 26 | #include <ieeefp.h> |
| 27 | #endif |
| 28 | |
| 29 | #include "math.h" |
| 30 | #include "math_private.h" |
| 31 | #include "fpmath.h" |
| 32 | #include "k_expl.h" |
| 33 | |
| 34 | #if LDBL_MAX_EXP != 0x4000 |
| 35 | /* We also require the usual expsign encoding. */ |
| 36 | #error "Unsupported long double format" |
| 37 | #endif |
| 38 | |
| 39 | #define BIAS (LDBL_MAX_EXP - 1) |
| 40 | |
| 41 | static const volatile double tiny = 1.0e-300; |
| 42 | static const double one = 1.0; |
| 43 | #if LDBL_MANT_DIG == 64 |
| 44 | /* |
| 45 | * Domain [-0.25, 0.25], range ~[-1.6304e-22, 1.6304e-22]: |
| 46 | * |tanh(x)/x - t(x)| < 2**-72.3 |
| 47 | */ |
| 48 | static const union IEEEl2bits |
| 49 | T3u = LD80C(0xaaaaaaaaaaaaaa9f, -2, -3.33333333333333333017e-1L); |
| 50 | #define T3 T3u.e |
| 51 | static const double |
| 52 | T5 = 1.3333333333333314e-1, /* 0x1111111111110a.0p-55 */ |
| 53 | T7 = -5.3968253968210485e-2, /* -0x1ba1ba1ba1a1a1.0p-57 */ |
| 54 | T9 = 2.1869488531393817e-2, /* 0x1664f488172022.0p-58 */ |
| 55 | T11 = -8.8632352345964591e-3, /* -0x1226e34bc138d5.0p-59 */ |
| 56 | T13 = 3.5921169709993771e-3, /* 0x1d6d371d3e400f.0p-61 */ |
| 57 | T15 = -1.4555786415756001e-3, /* -0x17d923aa63814d.0p-62 */ |
| 58 | T17 = 5.8645267876296793e-4, /* 0x13378589b85aa7.0p-63 */ |
| 59 | T19 = -2.1121033571392224e-4; /* -0x1baf0af80c4090.0p-65 */ |
| 60 | #elif LDBL_MANT_DIG == 113 |
| 61 | /* |
| 62 | * Domain [-0.25, 0.25], range ~[-2.4211e-37, 2.4211e-37]: |
| 63 | * |tanh(x)/x - t(x)| < 2**121.6 |
| 64 | */ |
| 65 | static const long double |
| 66 | T3 = -3.33333333333333333333333333333332980e-1L, /* -0x1555555555555555555555555554e.0p-114L */ |
| 67 | T5 = 1.33333333333333333333333333332707260e-1L, /* 0x1111111111111111111111110ab7b.0p-115L */ |
| 68 | T7 = -5.39682539682539682539682535723482314e-2L, /* -0x1ba1ba1ba1ba1ba1ba1ba17b5fc98.0p-117L */ |
| 69 | T9 = 2.18694885361552028218693591149061717e-2L, /* 0x1664f4882c10f9f32d6b1a12a25e5.0p-118L */ |
| 70 | T11 = -8.86323552990219656883762347736381851e-3L, /* -0x1226e355e6c23c8f5a5a0f386cb4d.0p-119L */ |
| 71 | T13 = 3.59212803657248101358314398220822722e-3L, /* 0x1d6d3d0e157ddfb403ad3637442c6.0p-121L */ |
| 72 | T15 = -1.45583438705131796512568010348874662e-3L; /* -0x17da36452b75e150c44cc34253b34.0p-122L */ |
| 73 | static const double |
| 74 | T17 = 5.9002744094556621e-4, /* 0x1355824803668e.0p-63 */ |
| 75 | T19 = -2.3912911424260516e-4, /* -0x1f57d7734c8dde.0p-65 */ |
| 76 | T21 = 9.6915379535512898e-5, /* 0x1967e18ad6a6ca.0p-66 */ |
| 77 | T23 = -3.9278322983156353e-5, /* -0x1497d8e6b75729.0p-67 */ |
| 78 | T25 = 1.5918887220143869e-5, /* 0x10b1319998cafa.0p-68 */ |
| 79 | T27 = -6.4514295231630956e-6, /* -0x1b0f2b71b218eb.0p-70 */ |
| 80 | T29 = 2.6120754043964365e-6, /* 0x15e963a3cf3a39.0p-71 */ |
| 81 | T31 = -1.0407567231003314e-6, /* -0x1176041e656869.0p-72 */ |
| 82 | T33 = 3.4744117554063574e-7; /* 0x1750fe732cab9c.0p-74 */ |
| 83 | #endif /* LDBL_MANT_DIG == 64 */ |
| 84 | |
| 85 | static inline long double |
| 86 | divl(long double a, long double b, long double c, long double d, |
| 87 | long double e, long double f) |
| 88 | { |
| 89 | long double inv, r; |
| 90 | float fr, fw; |
| 91 | |
| 92 | _2sumF(a, c); |
| 93 | b = b + c; |
| 94 | _2sumF(d, f); |
| 95 | e = e + f; |
| 96 | |
| 97 | inv = 1 / (d + e); |
| 98 | |
| 99 | r = (a + b) * inv; |
| 100 | fr = r; |
| 101 | r = fr; |
| 102 | |
| 103 | fw = d + e; |
| 104 | e = d - fw + e; |
| 105 | d = fw; |
| 106 | |
| 107 | r = r + (a - d * r + b - e * r) * inv; |
| 108 | |
| 109 | return r; |
| 110 | } |
| 111 | |
| 112 | long double |
| 113 | tanhl(long double x) |
| 114 | { |
| 115 | long double hi,lo,s,x2,x4,z; |
Elliott Hughes | 8da8ca4 | 2018-05-08 13:35:33 -0700 | [diff] [blame] | 116 | #if LDBL_MANT_DIG == 113 |
Elliott Hughes | c729d4f | 2014-09-12 16:09:40 -0700 | [diff] [blame] | 117 | double dx2; |
Elliott Hughes | 8da8ca4 | 2018-05-08 13:35:33 -0700 | [diff] [blame] | 118 | #endif |
Elliott Hughes | c729d4f | 2014-09-12 16:09:40 -0700 | [diff] [blame] | 119 | int16_t jx,ix; |
| 120 | |
| 121 | GET_LDBL_EXPSIGN(jx,x); |
| 122 | ix = jx&0x7fff; |
| 123 | |
| 124 | /* x is INF or NaN */ |
| 125 | if(ix>=0x7fff) { |
| 126 | if (jx>=0) return one/x+one; /* tanh(+-inf)=+-1 */ |
| 127 | else return one/x-one; /* tanh(NaN) = NaN */ |
| 128 | } |
| 129 | |
| 130 | ENTERI(); |
| 131 | |
| 132 | /* |x| < 40 */ |
| 133 | if (ix < 0x4004 || fabsl(x) < 40) { /* |x|<40 */ |
| 134 | if (__predict_false(ix<BIAS-(LDBL_MANT_DIG+1)/2)) { /* |x|<TINY */ |
| 135 | /* tanh(+-0) = +0; tanh(tiny) = tiny(-+) with inexact: */ |
| 136 | return (x == 0 ? x : (0x1p200 * x - x) * 0x1p-200); |
| 137 | } |
| 138 | if (ix<0x3ffd) { /* |x|<0.25 */ |
| 139 | x2 = x*x; |
| 140 | #if LDBL_MANT_DIG == 64 |
| 141 | x4 = x2*x2; |
| 142 | RETURNI(((T19*x2 + T17)*x4 + (T15*x2 + T13))*(x2*x*x2*x4*x4) + |
| 143 | ((T11*x2 + T9)*x4 + (T7*x2 + T5))*(x2*x*x2) + |
| 144 | T3*(x2*x) + x); |
| 145 | #elif LDBL_MANT_DIG == 113 |
| 146 | dx2 = x2; |
| 147 | #if 0 |
| 148 | RETURNI(((((((((((((((T33*dx2 + T31)*dx2 + T29)*dx2 + T27)*dx2 + |
| 149 | T25)*x2 + T23)*x2 + T21)*x2 + T19)*x2 + T17)*x2 + |
| 150 | T15)*x2 + T13)*x2 + T11)*x2 + T9)*x2 + T7)*x2 + T5)* |
| 151 | (x2*x*x2) + |
| 152 | T3*(x2*x) + x); |
| 153 | #else |
| 154 | long double q = ((((((((((((((T33*dx2 + T31)*dx2 + T29)*dx2 + T27)*dx2 + |
| 155 | T25)*x2 + T23)*x2 + T21)*x2 + T19)*x2 + T17)*x2 + |
| 156 | T15)*x2 + T13)*x2 + T11)*x2 + T9)*x2 + T7)*x2 + T5)* |
| 157 | (x2*x*x2); |
| 158 | RETURNI(q + T3*(x2*x) + x); |
| 159 | #endif |
| 160 | #endif |
| 161 | } |
| 162 | k_hexpl(2*fabsl(x), &hi, &lo); |
| 163 | if (ix<0x4001 && fabsl(x) < 1.5) /* |x|<1.5 */ |
| 164 | z = divl(hi, lo, -0.5, hi, lo, 0.5); |
| 165 | else |
| 166 | z = one - one/(lo+0.5+hi); |
| 167 | /* |x| >= 40, return +-1 */ |
| 168 | } else { |
| 169 | z = one - tiny; /* raise inexact flag */ |
| 170 | } |
| 171 | s = 1; |
| 172 | if (jx<0) s = -1; |
| 173 | RETURNI(s*z); |
| 174 | } |