1 /* Complex hyperbolic tangent for float types.
2    Copyright (C) 1997-2018 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
5 
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10 
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15 
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <http://www.gnu.org/licenses/>.  */
19 
20 #include "quadmath-imp.h"
21 
22 __complex128
ctanhq(__complex128 x)23 ctanhq (__complex128 x)
24 {
25   __complex128 res;
26 
27   if (__glibc_unlikely (!finiteq (__real__ x) || !finiteq (__imag__ x)))
28     {
29       if (isinfq (__real__ x))
30 	{
31 	  __real__ res = copysignq (1, __real__ x);
32 	  if (finiteq (__imag__ x) && fabsq (__imag__ x) > 1)
33 	    {
34 	      __float128 sinix, cosix;
35 	      sincosq (__imag__ x, &sinix, &cosix);
36 	      __imag__ res = copysignq (0, sinix * cosix);
37 	    }
38 	  else
39 	    __imag__ res = copysignq (0, __imag__ x);
40 	}
41       else if (__imag__ x == 0)
42 	{
43 	  res = x;
44 	}
45       else
46 	{
47 	  if (__real__ x == 0)
48 	    __real__ res = __real__ x;
49 	  else
50 	    __real__ res = nanq ("");
51 	  __imag__ res = nanq ("");
52 
53 	  if (isinfq (__imag__ x))
54 	    feraiseexcept (FE_INVALID);
55 	}
56     }
57   else
58     {
59       __float128 sinix, cosix;
60       __float128 den;
61       const int t = (int) ((FLT128_MAX_EXP - 1) * M_LN2q / 2);
62 
63       /* tanh(x+iy) = (sinh(2x) + i*sin(2y))/(cosh(2x) + cos(2y))
64 	 = (sinh(x)*cosh(x) + i*sin(y)*cos(y))/(sinh(x)^2 + cos(y)^2).  */
65 
66       if (__glibc_likely (fabsq (__imag__ x) > FLT128_MIN))
67 	{
68 	  sincosq (__imag__ x, &sinix, &cosix);
69 	}
70       else
71 	{
72 	  sinix = __imag__ x;
73 	  cosix = 1;
74 	}
75 
76       if (fabsq (__real__ x) > t)
77 	{
78 	  /* Avoid intermediate overflow when the imaginary part of
79 	     the result may be subnormal.  Ignoring negligible terms,
80 	     the real part is +/- 1, the imaginary part is
81 	     sin(y)*cos(y)/sinh(x)^2 = 4*sin(y)*cos(y)/exp(2x).  */
82 	  __float128 exp_2t = expq (2 * t);
83 
84 	  __real__ res = copysignq (1, __real__ x);
85 	  __imag__ res = 4 * sinix * cosix;
86 	  __real__ x = fabsq (__real__ x);
87 	  __real__ x -= t;
88 	  __imag__ res /= exp_2t;
89 	  if (__real__ x > t)
90 	    {
91 	      /* Underflow (original real part of x has absolute value
92 		 > 2t).  */
93 	      __imag__ res /= exp_2t;
94 	    }
95 	  else
96 	    __imag__ res /= expq (2 * __real__ x);
97 	}
98       else
99 	{
100 	  __float128 sinhrx, coshrx;
101 	  if (fabsq (__real__ x) > FLT128_MIN)
102 	    {
103 	      sinhrx = sinhq (__real__ x);
104 	      coshrx = coshq (__real__ x);
105 	    }
106 	  else
107 	    {
108 	      sinhrx = __real__ x;
109 	      coshrx = 1;
110 	    }
111 
112 	  if (fabsq (sinhrx) > fabsq (cosix) * FLT128_EPSILON)
113 	    den = sinhrx * sinhrx + cosix * cosix;
114 	  else
115 	    den = cosix * cosix;
116 	  __real__ res = sinhrx * coshrx / den;
117 	  __imag__ res = sinix * cosix / den;
118 	}
119       math_check_force_underflow_complex (res);
120     }
121 
122   return res;
123 }
124