12116Sjkh/* @(#)s_tanh.c 5.1 93/09/24 */
22116Sjkh/*
32116Sjkh * ====================================================
42116Sjkh * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
52116Sjkh *
62116Sjkh * Developed at SunPro, a Sun Microsystems, Inc. business.
72116Sjkh * Permission to use, copy, modify, and distribute this
88870Srgrimes * software is freely granted, provided that this notice
92116Sjkh * is preserved.
102116Sjkh * ====================================================
112116Sjkh */
122116Sjkh
13176451Sdas#include <sys/cdefs.h>
14176451Sdas__FBSDID("$FreeBSD$");
152116Sjkh
162116Sjkh/* Tanh(x)
172116Sjkh * Return the Hyperbolic Tangent of x
182116Sjkh *
192116Sjkh * Method :
202116Sjkh *				       x    -x
212116Sjkh *				      e  - e
222116Sjkh *	0. tanh(x) is defined to be -----------
232116Sjkh *				       x    -x
242116Sjkh *				      e  + e
252116Sjkh *	1. reduce x to non-negative by tanh(-x) = -tanh(x).
26160122Sbde *	2.  0      <= x <  2**-28 : tanh(x) := x with inexact if x != 0
272116Sjkh *					        -t
28160122Sbde *	    2**-28 <= x <  1      : tanh(x) := -----; t = expm1(-2x)
292116Sjkh *					       t + 2
302116Sjkh *						     2
31160122Sbde *	    1      <= x <  22     : tanh(x) := 1 - -----; t = expm1(2x)
322116Sjkh *						   t + 2
33160122Sbde *	    22     <= x <= INF    : tanh(x) := 1.
342116Sjkh *
352116Sjkh * Special cases:
362116Sjkh *	tanh(NaN) is NaN;
372116Sjkh *	only tanh(0)=0 is exact for finite argument.
382116Sjkh */
392116Sjkh
402116Sjkh#include "math.h"
412116Sjkh#include "math_private.h"
422116Sjkh
43160122Sbdestatic const double one = 1.0, two = 2.0, tiny = 1.0e-300, huge = 1.0e300;
442116Sjkh
4597413Salfreddouble
4697413Salfredtanh(double x)
472116Sjkh{
482116Sjkh	double t,z;
492116Sjkh	int32_t jx,ix;
502116Sjkh
512116Sjkh	GET_HIGH_WORD(jx,x);
522116Sjkh	ix = jx&0x7fffffff;
532116Sjkh
542116Sjkh    /* x is INF or NaN */
558870Srgrimes	if(ix>=0x7ff00000) {
562116Sjkh	    if (jx>=0) return one/x+one;    /* tanh(+-inf)=+-1 */
572116Sjkh	    else       return one/x-one;    /* tanh(NaN) = NaN */
582116Sjkh	}
592116Sjkh
602116Sjkh    /* |x| < 22 */
612116Sjkh	if (ix < 0x40360000) {		/* |x|<22 */
62160122Sbde	    if (ix<0x3e300000) {	/* |x|<2**-28 */
63160122Sbde		if(huge+x>one) return x; /* tanh(tiny) = tiny with inexact */
64160122Sbde	    }
652116Sjkh	    if (ix>=0x3ff00000) {	/* |x|>=1  */
662116Sjkh		t = expm1(two*fabs(x));
672116Sjkh		z = one - two/(t+two);
682116Sjkh	    } else {
692116Sjkh	        t = expm1(-two*fabs(x));
702116Sjkh	        z= -t/(t+two);
712116Sjkh	    }
72160122Sbde    /* |x| >= 22, return +-1 */
732116Sjkh	} else {
74160122Sbde	    z = one - tiny;		/* raise inexact flag */
752116Sjkh	}
762116Sjkh	return (jx>=0)? z: -z;
772116Sjkh}
78