xref: /netbsd-src/external/gpl3/gcc.old/dist/libquadmath/math/ctanq.c (revision 627f7eb200a4419d89b531d55fccd2ee3ffdcde0)
1 /* Complex tangent function for a complex float type.
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
ctanq(__complex128 x)23 ctanq (__complex128 x)
24 {
25   __complex128 res;
26 
27   if (__glibc_unlikely (!finiteq (__real__ x) || !finiteq (__imag__ x)))
28     {
29       if (isinfq (__imag__ x))
30 	{
31 	  if (finiteq (__real__ x) && fabsq (__real__ x) > 1)
32 	    {
33 	      __float128 sinrx, cosrx;
34 	      sincosq (__real__ x, &sinrx, &cosrx);
35 	      __real__ res = copysignq (0, sinrx * cosrx);
36 	    }
37 	  else
38 	    __real__ res = copysignq (0, __real__ x);
39 	  __imag__ res = copysignq (1, __imag__ x);
40 	}
41       else if (__real__ x == 0)
42 	{
43 	  res = x;
44 	}
45       else
46 	{
47 	  __real__ res = nanq ("");
48 	  if (__imag__ x == 0)
49 	    __imag__ res = __imag__ x;
50 	  else
51 	    __imag__ res = nanq ("");
52 
53 	  if (isinfq (__real__ x))
54 	    feraiseexcept (FE_INVALID);
55 	}
56     }
57   else
58     {
59       __float128 sinrx, cosrx;
60       __float128 den;
61       const int t = (int) ((FLT128_MAX_EXP - 1) * M_LN2q / 2);
62 
63       /* tan(x+iy) = (sin(2x) + i*sinh(2y))/(cos(2x) + cosh(2y))
64 	 = (sin(x)*cos(x) + i*sinh(y)*cosh(y)/(cos(x)^2 + sinh(y)^2). */
65 
66       if (__glibc_likely (fabsq (__real__ x) > FLT128_MIN))
67 	{
68 	  sincosq (__real__ x, &sinrx, &cosrx);
69 	}
70       else
71 	{
72 	  sinrx = __real__ x;
73 	  cosrx = 1;
74 	}
75 
76       if (fabsq (__imag__ x) > t)
77 	{
78 	  /* Avoid intermediate overflow when the real part of the
79 	     result may be subnormal.  Ignoring negligible terms, the
80 	     imaginary part is +/- 1, the real part is
81 	     sin(x)*cos(x)/sinh(y)^2 = 4*sin(x)*cos(x)/exp(2y).  */
82 	  __float128 exp_2t = expq (2 * t);
83 
84 	  __imag__ res = copysignq (1, __imag__ x);
85 	  __real__ res = 4 * sinrx * cosrx;
86 	  __imag__ x = fabsq (__imag__ x);
87 	  __imag__ x -= t;
88 	  __real__ res /= exp_2t;
89 	  if (__imag__ x > t)
90 	    {
91 	      /* Underflow (original imaginary part of x has absolute
92 		 value > 2t).  */
93 	      __real__ res /= exp_2t;
94 	    }
95 	  else
96 	    __real__ res /= expq (2 * __imag__ x);
97 	}
98       else
99 	{
100 	  __float128 sinhix, coshix;
101 	  if (fabsq (__imag__ x) > FLT128_MIN)
102 	    {
103 	      sinhix = sinhq (__imag__ x);
104 	      coshix = coshq (__imag__ x);
105 	    }
106 	  else
107 	    {
108 	      sinhix = __imag__ x;
109 	      coshix = 1;
110 	    }
111 
112 	  if (fabsq (sinhix) > fabsq (cosrx) * FLT128_EPSILON)
113 	    den = cosrx * cosrx + sinhix * sinhix;
114 	  else
115 	    den = cosrx * cosrx;
116 	  __real__ res = sinrx * cosrx / den;
117 	  __imag__ res = sinhix * coshix / den;
118 	}
119       math_check_force_underflow_complex (res);
120     }
121 
122   return res;
123 }
124