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