xref: /dflybsd-src/contrib/mpc/src/tan.c (revision 04febcfb30580676d3e95f58a16c5137ee478b32)
1*d30dc8cbSJohn Marino /* mpc_tan -- tangent of a complex number.
2*d30dc8cbSJohn Marino 
3*d30dc8cbSJohn Marino Copyright (C) 2008, 2009, 2010, 2011 INRIA
4*d30dc8cbSJohn Marino 
5*d30dc8cbSJohn Marino This file is part of GNU MPC.
6*d30dc8cbSJohn Marino 
7*d30dc8cbSJohn Marino GNU MPC is free software; you can redistribute it and/or modify it under
8*d30dc8cbSJohn Marino the terms of the GNU Lesser General Public License as published by the
9*d30dc8cbSJohn Marino Free Software Foundation; either version 3 of the License, or (at your
10*d30dc8cbSJohn Marino option) any later version.
11*d30dc8cbSJohn Marino 
12*d30dc8cbSJohn Marino GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
13*d30dc8cbSJohn Marino WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14*d30dc8cbSJohn Marino FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
15*d30dc8cbSJohn Marino more details.
16*d30dc8cbSJohn Marino 
17*d30dc8cbSJohn Marino You should have received a copy of the GNU Lesser General Public License
18*d30dc8cbSJohn Marino along with this program. If not, see http://www.gnu.org/licenses/ .
19*d30dc8cbSJohn Marino */
20*d30dc8cbSJohn Marino 
21*d30dc8cbSJohn Marino #include <stdio.h>    /* for MPC_ASSERT */
22*d30dc8cbSJohn Marino #include <limits.h>
23*d30dc8cbSJohn Marino #include "mpc-impl.h"
24*d30dc8cbSJohn Marino 
25*d30dc8cbSJohn Marino int
mpc_tan(mpc_ptr rop,mpc_srcptr op,mpc_rnd_t rnd)26*d30dc8cbSJohn Marino mpc_tan (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd)
27*d30dc8cbSJohn Marino {
28*d30dc8cbSJohn Marino   mpc_t x, y;
29*d30dc8cbSJohn Marino   mpfr_prec_t prec;
30*d30dc8cbSJohn Marino   mpfr_exp_t err;
31*d30dc8cbSJohn Marino   int ok = 0;
32*d30dc8cbSJohn Marino   int inex;
33*d30dc8cbSJohn Marino 
34*d30dc8cbSJohn Marino   /* special values */
35*d30dc8cbSJohn Marino   if (!mpc_fin_p (op))
36*d30dc8cbSJohn Marino     {
37*d30dc8cbSJohn Marino       if (mpfr_nan_p (mpc_realref (op)))
38*d30dc8cbSJohn Marino         {
39*d30dc8cbSJohn Marino           if (mpfr_inf_p (mpc_imagref (op)))
40*d30dc8cbSJohn Marino             /* tan(NaN -i*Inf) = +/-0 -i */
41*d30dc8cbSJohn Marino             /* tan(NaN +i*Inf) = +/-0 +i */
42*d30dc8cbSJohn Marino             {
43*d30dc8cbSJohn Marino               /* exact unless 1 is not in exponent range */
44*d30dc8cbSJohn Marino               inex = mpc_set_si_si (rop, 0,
45*d30dc8cbSJohn Marino                                     (MPFR_SIGN (mpc_imagref (op)) < 0) ? -1 : +1,
46*d30dc8cbSJohn Marino                                     rnd);
47*d30dc8cbSJohn Marino             }
48*d30dc8cbSJohn Marino           else
49*d30dc8cbSJohn Marino             /* tan(NaN +i*y) = NaN +i*NaN, when y is finite */
50*d30dc8cbSJohn Marino             /* tan(NaN +i*NaN) = NaN +i*NaN */
51*d30dc8cbSJohn Marino             {
52*d30dc8cbSJohn Marino               mpfr_set_nan (mpc_realref (rop));
53*d30dc8cbSJohn Marino               mpfr_set_nan (mpc_imagref (rop));
54*d30dc8cbSJohn Marino               inex = MPC_INEX (0, 0); /* always exact */
55*d30dc8cbSJohn Marino             }
56*d30dc8cbSJohn Marino         }
57*d30dc8cbSJohn Marino       else if (mpfr_nan_p (mpc_imagref (op)))
58*d30dc8cbSJohn Marino         {
59*d30dc8cbSJohn Marino           if (mpfr_cmp_ui (mpc_realref (op), 0) == 0)
60*d30dc8cbSJohn Marino             /* tan(-0 +i*NaN) = -0 +i*NaN */
61*d30dc8cbSJohn Marino             /* tan(+0 +i*NaN) = +0 +i*NaN */
62*d30dc8cbSJohn Marino             {
63*d30dc8cbSJohn Marino               mpc_set (rop, op, rnd);
64*d30dc8cbSJohn Marino               inex = MPC_INEX (0, 0); /* always exact */
65*d30dc8cbSJohn Marino             }
66*d30dc8cbSJohn Marino           else
67*d30dc8cbSJohn Marino             /* tan(x +i*NaN) = NaN +i*NaN, when x != 0 */
68*d30dc8cbSJohn Marino             {
69*d30dc8cbSJohn Marino               mpfr_set_nan (mpc_realref (rop));
70*d30dc8cbSJohn Marino               mpfr_set_nan (mpc_imagref (rop));
71*d30dc8cbSJohn Marino               inex = MPC_INEX (0, 0); /* always exact */
72*d30dc8cbSJohn Marino             }
73*d30dc8cbSJohn Marino         }
74*d30dc8cbSJohn Marino       else if (mpfr_inf_p (mpc_realref (op)))
75*d30dc8cbSJohn Marino         {
76*d30dc8cbSJohn Marino           if (mpfr_inf_p (mpc_imagref (op)))
77*d30dc8cbSJohn Marino             /* tan(-Inf -i*Inf) = -/+0 -i */
78*d30dc8cbSJohn Marino             /* tan(-Inf +i*Inf) = -/+0 +i */
79*d30dc8cbSJohn Marino             /* tan(+Inf -i*Inf) = +/-0 -i */
80*d30dc8cbSJohn Marino             /* tan(+Inf +i*Inf) = +/-0 +i */
81*d30dc8cbSJohn Marino             {
82*d30dc8cbSJohn Marino               const int sign_re = mpfr_signbit (mpc_realref (op));
83*d30dc8cbSJohn Marino               int inex_im;
84*d30dc8cbSJohn Marino 
85*d30dc8cbSJohn Marino               mpfr_set_ui (mpc_realref (rop), 0, MPC_RND_RE (rnd));
86*d30dc8cbSJohn Marino               mpfr_setsign (mpc_realref (rop), mpc_realref (rop), sign_re, GMP_RNDN);
87*d30dc8cbSJohn Marino 
88*d30dc8cbSJohn Marino               /* exact, unless 1 is not in exponent range */
89*d30dc8cbSJohn Marino               inex_im = mpfr_set_si (mpc_imagref (rop),
90*d30dc8cbSJohn Marino                                      mpfr_signbit (mpc_imagref (op)) ? -1 : +1,
91*d30dc8cbSJohn Marino                                      MPC_RND_IM (rnd));
92*d30dc8cbSJohn Marino               inex = MPC_INEX (0, inex_im);
93*d30dc8cbSJohn Marino             }
94*d30dc8cbSJohn Marino           else
95*d30dc8cbSJohn Marino             /* tan(-Inf +i*y) = tan(+Inf +i*y) = NaN +i*NaN, when y is
96*d30dc8cbSJohn Marino                finite */
97*d30dc8cbSJohn Marino             {
98*d30dc8cbSJohn Marino               mpfr_set_nan (mpc_realref (rop));
99*d30dc8cbSJohn Marino               mpfr_set_nan (mpc_imagref (rop));
100*d30dc8cbSJohn Marino               inex = MPC_INEX (0, 0); /* always exact */
101*d30dc8cbSJohn Marino             }
102*d30dc8cbSJohn Marino         }
103*d30dc8cbSJohn Marino       else
104*d30dc8cbSJohn Marino         /* tan(x -i*Inf) = +0*sin(x)*cos(x) -i, when x is finite */
105*d30dc8cbSJohn Marino         /* tan(x +i*Inf) = +0*sin(x)*cos(x) +i, when x is finite */
106*d30dc8cbSJohn Marino         {
107*d30dc8cbSJohn Marino           mpfr_t c;
108*d30dc8cbSJohn Marino           mpfr_t s;
109*d30dc8cbSJohn Marino           int inex_im;
110*d30dc8cbSJohn Marino 
111*d30dc8cbSJohn Marino           mpfr_init (c);
112*d30dc8cbSJohn Marino           mpfr_init (s);
113*d30dc8cbSJohn Marino 
114*d30dc8cbSJohn Marino           mpfr_sin_cos (s, c, mpc_realref (op), GMP_RNDN);
115*d30dc8cbSJohn Marino           mpfr_set_ui (mpc_realref (rop), 0, MPC_RND_RE (rnd));
116*d30dc8cbSJohn Marino           mpfr_setsign (mpc_realref (rop), mpc_realref (rop),
117*d30dc8cbSJohn Marino                         mpfr_signbit (c) != mpfr_signbit (s), GMP_RNDN);
118*d30dc8cbSJohn Marino           /* exact, unless 1 is not in exponent range */
119*d30dc8cbSJohn Marino           inex_im = mpfr_set_si (mpc_imagref (rop),
120*d30dc8cbSJohn Marino                                  (mpfr_signbit (mpc_imagref (op)) ? -1 : +1),
121*d30dc8cbSJohn Marino                                  MPC_RND_IM (rnd));
122*d30dc8cbSJohn Marino           inex = MPC_INEX (0, inex_im);
123*d30dc8cbSJohn Marino 
124*d30dc8cbSJohn Marino           mpfr_clear (s);
125*d30dc8cbSJohn Marino           mpfr_clear (c);
126*d30dc8cbSJohn Marino         }
127*d30dc8cbSJohn Marino 
128*d30dc8cbSJohn Marino       return inex;
129*d30dc8cbSJohn Marino     }
130*d30dc8cbSJohn Marino 
131*d30dc8cbSJohn Marino   if (mpfr_zero_p (mpc_realref (op)))
132*d30dc8cbSJohn Marino     /* tan(-0 -i*y) = -0 +i*tanh(y), when y is finite. */
133*d30dc8cbSJohn Marino     /* tan(+0 +i*y) = +0 +i*tanh(y), when y is finite. */
134*d30dc8cbSJohn Marino     {
135*d30dc8cbSJohn Marino       int inex_im;
136*d30dc8cbSJohn Marino 
137*d30dc8cbSJohn Marino       mpfr_set (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd));
138*d30dc8cbSJohn Marino       inex_im = mpfr_tanh (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd));
139*d30dc8cbSJohn Marino 
140*d30dc8cbSJohn Marino       return MPC_INEX (0, inex_im);
141*d30dc8cbSJohn Marino     }
142*d30dc8cbSJohn Marino 
143*d30dc8cbSJohn Marino   if (mpfr_zero_p (mpc_imagref (op)))
144*d30dc8cbSJohn Marino     /* tan(x -i*0) = tan(x) -i*0, when x is finite. */
145*d30dc8cbSJohn Marino     /* tan(x +i*0) = tan(x) +i*0, when x is finite. */
146*d30dc8cbSJohn Marino     {
147*d30dc8cbSJohn Marino       int inex_re;
148*d30dc8cbSJohn Marino 
149*d30dc8cbSJohn Marino       inex_re = mpfr_tan (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd));
150*d30dc8cbSJohn Marino       mpfr_set (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd));
151*d30dc8cbSJohn Marino 
152*d30dc8cbSJohn Marino       return MPC_INEX (inex_re, 0);
153*d30dc8cbSJohn Marino     }
154*d30dc8cbSJohn Marino 
155*d30dc8cbSJohn Marino   /* ordinary (non-zero) numbers */
156*d30dc8cbSJohn Marino 
157*d30dc8cbSJohn Marino   /* tan(op) = sin(op) / cos(op).
158*d30dc8cbSJohn Marino 
159*d30dc8cbSJohn Marino      We use the following algorithm with rounding away from 0 for all
160*d30dc8cbSJohn Marino      operations, and working precision w:
161*d30dc8cbSJohn Marino 
162*d30dc8cbSJohn Marino      (1) x = A(sin(op))
163*d30dc8cbSJohn Marino      (2) y = A(cos(op))
164*d30dc8cbSJohn Marino      (3) z = A(x/y)
165*d30dc8cbSJohn Marino 
166*d30dc8cbSJohn Marino      the error on Im(z) is at most 81 ulp,
167*d30dc8cbSJohn Marino      the error on Re(z) is at most
168*d30dc8cbSJohn Marino      7 ulp if k < 2,
169*d30dc8cbSJohn Marino      8 ulp if k = 2,
170*d30dc8cbSJohn Marino      else 5+k ulp, where
171*d30dc8cbSJohn Marino      k = Exp(Re(x))+Exp(Re(y))-2min{Exp(Re(y)), Exp(Im(y))}-Exp(Re(x/y))
172*d30dc8cbSJohn Marino      see proof in algorithms.tex.
173*d30dc8cbSJohn Marino   */
174*d30dc8cbSJohn Marino 
175*d30dc8cbSJohn Marino   prec = MPC_MAX_PREC(rop);
176*d30dc8cbSJohn Marino 
177*d30dc8cbSJohn Marino   mpc_init2 (x, 2);
178*d30dc8cbSJohn Marino   mpc_init2 (y, 2);
179*d30dc8cbSJohn Marino 
180*d30dc8cbSJohn Marino   err = 7;
181*d30dc8cbSJohn Marino 
182*d30dc8cbSJohn Marino   do
183*d30dc8cbSJohn Marino     {
184*d30dc8cbSJohn Marino       mpfr_exp_t k, exr, eyr, eyi, ezr;
185*d30dc8cbSJohn Marino 
186*d30dc8cbSJohn Marino       ok = 0;
187*d30dc8cbSJohn Marino 
188*d30dc8cbSJohn Marino       /* FIXME: prevent addition overflow */
189*d30dc8cbSJohn Marino       prec += mpc_ceil_log2 (prec) + err;
190*d30dc8cbSJohn Marino       mpc_set_prec (x, prec);
191*d30dc8cbSJohn Marino       mpc_set_prec (y, prec);
192*d30dc8cbSJohn Marino 
193*d30dc8cbSJohn Marino       /* rounding away from zero: except in the cases x=0 or y=0 (processed
194*d30dc8cbSJohn Marino          above), sin x and cos y are never exact, so rounding away from 0 is
195*d30dc8cbSJohn Marino          rounding towards 0 and adding one ulp to the absolute value */
196*d30dc8cbSJohn Marino       mpc_sin_cos (x, y, op, MPC_RNDZZ, MPC_RNDZZ);
197*d30dc8cbSJohn Marino       MPFR_ADD_ONE_ULP (mpc_realref (x));
198*d30dc8cbSJohn Marino       MPFR_ADD_ONE_ULP (mpc_imagref (x));
199*d30dc8cbSJohn Marino       MPFR_ADD_ONE_ULP (mpc_realref (y));
200*d30dc8cbSJohn Marino       MPFR_ADD_ONE_ULP (mpc_imagref (y));
201*d30dc8cbSJohn Marino       MPC_ASSERT (mpfr_zero_p (mpc_realref (x)) == 0);
202*d30dc8cbSJohn Marino 
203*d30dc8cbSJohn Marino       if (   mpfr_inf_p (mpc_realref (x)) || mpfr_inf_p (mpc_imagref (x))
204*d30dc8cbSJohn Marino           || mpfr_inf_p (mpc_realref (y)) || mpfr_inf_p (mpc_imagref (y))) {
205*d30dc8cbSJohn Marino          /* If the real or imaginary part of x is infinite, it means that
206*d30dc8cbSJohn Marino             Im(op) was large, in which case the result is
207*d30dc8cbSJohn Marino             sign(tan(Re(op)))*0 + sign(Im(op))*I,
208*d30dc8cbSJohn Marino             where sign(tan(Re(op))) = sign(Re(x))*sign(Re(y)). */
209*d30dc8cbSJohn Marino           int inex_re, inex_im;
210*d30dc8cbSJohn Marino           mpfr_set_ui (mpc_realref (rop), 0, GMP_RNDN);
211*d30dc8cbSJohn Marino           if (mpfr_sgn (mpc_realref (x)) * mpfr_sgn (mpc_realref (y)) < 0)
212*d30dc8cbSJohn Marino             {
213*d30dc8cbSJohn Marino               mpfr_neg (mpc_realref (rop), mpc_realref (rop), GMP_RNDN);
214*d30dc8cbSJohn Marino               inex_re = 1;
215*d30dc8cbSJohn Marino             }
216*d30dc8cbSJohn Marino           else
217*d30dc8cbSJohn Marino             inex_re = -1; /* +0 is rounded down */
218*d30dc8cbSJohn Marino           if (mpfr_sgn (mpc_imagref (op)) > 0)
219*d30dc8cbSJohn Marino             {
220*d30dc8cbSJohn Marino               mpfr_set_ui (mpc_imagref (rop), 1, GMP_RNDN);
221*d30dc8cbSJohn Marino               inex_im = 1;
222*d30dc8cbSJohn Marino             }
223*d30dc8cbSJohn Marino           else
224*d30dc8cbSJohn Marino             {
225*d30dc8cbSJohn Marino               mpfr_set_si (mpc_imagref (rop), -1, GMP_RNDN);
226*d30dc8cbSJohn Marino               inex_im = -1;
227*d30dc8cbSJohn Marino             }
228*d30dc8cbSJohn Marino           inex = MPC_INEX(inex_re, inex_im);
229*d30dc8cbSJohn Marino           goto end;
230*d30dc8cbSJohn Marino         }
231*d30dc8cbSJohn Marino 
232*d30dc8cbSJohn Marino       exr = mpfr_get_exp (mpc_realref (x));
233*d30dc8cbSJohn Marino       eyr = mpfr_get_exp (mpc_realref (y));
234*d30dc8cbSJohn Marino       eyi = mpfr_get_exp (mpc_imagref (y));
235*d30dc8cbSJohn Marino 
236*d30dc8cbSJohn Marino       /* some parts of the quotient may be exact */
237*d30dc8cbSJohn Marino       inex = mpc_div (x, x, y, MPC_RNDZZ);
238*d30dc8cbSJohn Marino       /* OP is no pure real nor pure imaginary, so in theory the real and
239*d30dc8cbSJohn Marino          imaginary parts of its tangent cannot be null. However due to
240*d30dc8cbSJohn Marino          rouding errors this might happen. Consider for example
241*d30dc8cbSJohn Marino          tan(1+14*I) = 1.26e-10 + 1.00*I. For small precision sin(op) and
242*d30dc8cbSJohn Marino          cos(op) differ only by a factor I, thus after mpc_div x = I and
243*d30dc8cbSJohn Marino          its real part is zero. */
244*d30dc8cbSJohn Marino       if (mpfr_zero_p (mpc_realref (x)) || mpfr_zero_p (mpc_imagref (x)))
245*d30dc8cbSJohn Marino         {
246*d30dc8cbSJohn Marino           err = prec; /* double precision */
247*d30dc8cbSJohn Marino           continue;
248*d30dc8cbSJohn Marino         }
249*d30dc8cbSJohn Marino       if (MPC_INEX_RE (inex))
250*d30dc8cbSJohn Marino          MPFR_ADD_ONE_ULP (mpc_realref (x));
251*d30dc8cbSJohn Marino       if (MPC_INEX_IM (inex))
252*d30dc8cbSJohn Marino          MPFR_ADD_ONE_ULP (mpc_imagref (x));
253*d30dc8cbSJohn Marino       MPC_ASSERT (mpfr_zero_p (mpc_realref (x)) == 0);
254*d30dc8cbSJohn Marino       ezr = mpfr_get_exp (mpc_realref (x));
255*d30dc8cbSJohn Marino 
256*d30dc8cbSJohn Marino       /* FIXME: compute
257*d30dc8cbSJohn Marino          k = Exp(Re(x))+Exp(Re(y))-2min{Exp(Re(y)), Exp(Im(y))}-Exp(Re(x/y))
258*d30dc8cbSJohn Marino          avoiding overflow */
259*d30dc8cbSJohn Marino       k = exr - ezr + MPC_MAX(-eyr, eyr - 2 * eyi);
260*d30dc8cbSJohn Marino       err = k < 2 ? 7 : (k == 2 ? 8 : (5 + k));
261*d30dc8cbSJohn Marino 
262*d30dc8cbSJohn Marino       /* Can the real part be rounded? */
263*d30dc8cbSJohn Marino       ok = (!mpfr_number_p (mpc_realref (x)))
264*d30dc8cbSJohn Marino            || mpfr_can_round (mpc_realref(x), prec - err, GMP_RNDN, GMP_RNDZ,
265*d30dc8cbSJohn Marino                       MPC_PREC_RE(rop) + (MPC_RND_RE(rnd) == GMP_RNDN));
266*d30dc8cbSJohn Marino 
267*d30dc8cbSJohn Marino       if (ok)
268*d30dc8cbSJohn Marino         {
269*d30dc8cbSJohn Marino           /* Can the imaginary part be rounded? */
270*d30dc8cbSJohn Marino           ok = (!mpfr_number_p (mpc_imagref (x)))
271*d30dc8cbSJohn Marino                || mpfr_can_round (mpc_imagref(x), prec - 6, GMP_RNDN, GMP_RNDZ,
272*d30dc8cbSJohn Marino                       MPC_PREC_IM(rop) + (MPC_RND_IM(rnd) == GMP_RNDN));
273*d30dc8cbSJohn Marino         }
274*d30dc8cbSJohn Marino     }
275*d30dc8cbSJohn Marino   while (ok == 0);
276*d30dc8cbSJohn Marino 
277*d30dc8cbSJohn Marino   inex = mpc_set (rop, x, rnd);
278*d30dc8cbSJohn Marino 
279*d30dc8cbSJohn Marino  end:
280*d30dc8cbSJohn Marino   mpc_clear (x);
281*d30dc8cbSJohn Marino   mpc_clear (y);
282*d30dc8cbSJohn Marino 
283*d30dc8cbSJohn Marino   return inex;
284*d30dc8cbSJohn Marino }
285