1 /* mpc_tan -- tangent of a complex number.
3 Copyright (C) 2008, 2009 Philippe Th\'eveny, Andreas Enge
5 This file is part of the MPC Library.
7 The MPC Library is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or (at your
10 option) any later version.
12 The MPC Library is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with the MPC Library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
20 MA 02111-1307, USA. */
25 mpc_tan (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd)
34 if (!mpfr_number_p (MPC_RE (op)) || !mpfr_number_p (MPC_IM (op)))
36 if (mpfr_nan_p (MPC_RE (op)))
38 if (mpfr_inf_p (MPC_IM (op)))
39 /* tan(NaN -i*Inf) = +/-0 -i */
40 /* tan(NaN +i*Inf) = +/-0 +i */
42 /* exact unless 1 is not in exponent range */
43 inex = mpc_set_si_si (rop, 0,
44 (MPFR_SIGN (MPC_IM (op)) < 0) ? -1 : +1,
48 /* tan(NaN +i*y) = NaN +i*NaN, when y is finite */
49 /* tan(NaN +i*NaN) = NaN +i*NaN */
51 mpfr_set_nan (MPC_RE (rop));
52 mpfr_set_nan (MPC_IM (rop));
53 inex = MPC_INEX (0, 0); /* always exact */
56 else if (mpfr_nan_p (MPC_IM (op)))
58 if (mpfr_cmp_ui (MPC_RE (op), 0) == 0)
59 /* tan(-0 +i*NaN) = -0 +i*NaN */
60 /* tan(+0 +i*NaN) = +0 +i*NaN */
62 mpc_set (rop, op, rnd);
63 inex = MPC_INEX (0, 0); /* always exact */
66 /* tan(x +i*NaN) = NaN +i*NaN, when x != 0 */
68 mpfr_set_nan (MPC_RE (rop));
69 mpfr_set_nan (MPC_IM (rop));
70 inex = MPC_INEX (0, 0); /* always exact */
73 else if (mpfr_inf_p (MPC_RE (op)))
75 if (mpfr_inf_p (MPC_IM (op)))
76 /* tan(-Inf -i*Inf) = -/+0 -i */
77 /* tan(-Inf +i*Inf) = -/+0 +i */
78 /* tan(+Inf -i*Inf) = +/-0 -i */
79 /* tan(+Inf +i*Inf) = +/-0 +i */
81 const int sign_re = mpfr_signbit (MPC_RE (op));
84 mpfr_set_ui (MPC_RE (rop), 0, MPC_RND_RE (rnd));
85 mpfr_setsign (MPC_RE (rop), MPC_RE (rop), sign_re, GMP_RNDN);
87 /* exact, unless 1 is not in exponent range */
88 inex_im = mpfr_set_si (MPC_IM (rop),
89 mpfr_signbit (MPC_IM (op)) ? -1 : +1,
91 inex = MPC_INEX (0, inex_im);
94 /* tan(-Inf +i*y) = tan(+Inf +i*y) = NaN +i*NaN, when y is
97 mpfr_set_nan (MPC_RE (rop));
98 mpfr_set_nan (MPC_IM (rop));
99 inex = MPC_INEX (0, 0); /* always exact */
103 /* tan(x -i*Inf) = +0*sin(x)*cos(x) -i, when x is finite */
104 /* tan(x +i*Inf) = +0*sin(x)*cos(x) +i, when x is finite */
113 mpfr_sin_cos (s, c, MPC_RE (op), GMP_RNDN);
114 mpfr_set_ui (MPC_RE (rop), 0, MPC_RND_RE (rnd));
115 mpfr_setsign (MPC_RE (rop), MPC_RE (rop),
116 mpfr_signbit (c) != mpfr_signbit (s), GMP_RNDN);
117 /* exact, unless 1 is not in exponent range */
118 inex_im = mpfr_set_si (MPC_IM (rop),
119 (mpfr_signbit (MPC_IM (op)) ? -1 : +1),
121 inex = MPC_INEX (0, inex_im);
130 if (mpfr_zero_p (MPC_RE (op)))
131 /* tan(-0 -i*y) = -0 +i*tanh(y), when y is finite. */
132 /* tan(+0 +i*y) = +0 +i*tanh(y), when y is finite. */
136 mpfr_set (MPC_RE (rop), MPC_RE (op), MPC_RND_RE (rnd));
137 inex_im = mpfr_tanh (MPC_IM (rop), MPC_IM (op), MPC_RND_IM (rnd));
139 return MPC_INEX (0, inex_im);
142 if (mpfr_zero_p (MPC_IM (op)))
143 /* tan(x -i*0) = tan(x) -i*0, when x is finite. */
144 /* tan(x +i*0) = tan(x) +i*0, when x is finite. */
148 inex_re = mpfr_tan (MPC_RE (rop), MPC_RE (op), MPC_RND_RE (rnd));
149 mpfr_set (MPC_IM (rop), MPC_IM (op), MPC_RND_IM (rnd));
151 return MPC_INEX (inex_re, 0);
154 /* ordinary (non-zero) numbers */
156 /* tan(op) = sin(op) / cos(op).
158 We use the following algorithm with rounding away from 0 for all
159 operations, and working precision w:
165 the error on Im(z) is at most 81 ulp,
166 the error on Re(z) is at most
170 k = Exp(Re(x))+Exp(Re(y))-2min{Exp(Re(y)), Exp(Im(y))}-Exp(Re(x/y))
171 see proof in algorithms.tex.
174 prec = MPC_MAX_PREC(rop);
184 mp_exp_t exr, eyr, eyi, ezr;
188 /* FIXME: prevent addition overflow */
189 prec += mpc_ceil_log2 (prec) + err;
190 mpc_set_prec (x, prec);
191 mpc_set_prec (y, prec);
193 /* rounding away from zero: except in the cases x=0 or y=0 (processed
194 above), sin x and cos y are never exact, so rounding away from 0 is
195 rounding towards 0 and adding one ulp to the absolute value */
196 mpc_sin (x, op, MPC_RNDZZ);
197 mpfr_signbit (MPC_RE (x)) ?
198 mpfr_nextbelow (MPC_RE (x)) : mpfr_nextabove (MPC_RE (x));
199 mpfr_signbit (MPC_IM (x)) ?
200 mpfr_nextbelow (MPC_IM (x)) : mpfr_nextabove (MPC_IM (x));
201 exr = mpfr_get_exp (MPC_RE (x));
202 mpc_cos (y, op, MPC_RNDZZ);
203 mpfr_signbit (MPC_RE (y)) ?
204 mpfr_nextbelow (MPC_RE (y)) : mpfr_nextabove (MPC_RE (y));
205 mpfr_signbit (MPC_IM (y)) ?
206 mpfr_nextbelow (MPC_IM (y)) : mpfr_nextabove (MPC_IM (y));
207 eyr = mpfr_get_exp (MPC_RE (y));
208 eyi = mpfr_get_exp (MPC_IM (y));
210 /* some parts of the quotient may be exact */
211 inex = mpc_div (x, x, y, MPC_RNDZZ);
212 /* OP is no pure real nor pure imaginary, so in theory the real and
213 imaginary parts of its tangent cannot be null. However due to
214 rouding errors this might happen. Consider for example
215 tan(1+14*I) = 1.26e-10 + 1.00*I. For small precision sin(op) and
216 cos(op) differ only by a factor I, thus after mpc_div x = I and
217 its real part is zero. */
218 if (mpfr_zero_p (MPC_RE (x)) || mpfr_zero_p (MPC_IM (x)))
220 err = prec; /* double precision */
223 if (MPC_INEX_RE (inex))
224 mpfr_signbit (MPC_RE (x)) ?
225 mpfr_nextbelow (MPC_RE (x)) : mpfr_nextabove (MPC_RE (x));
226 if (MPC_INEX_IM (inex))
227 mpfr_signbit (MPC_IM (x)) ?
228 mpfr_nextbelow (MPC_IM (x)) : mpfr_nextabove (MPC_IM (x));
229 ezr = mpfr_get_exp (MPC_RE (x));
232 k = Exp(Re(x))+Exp(Re(y))-2min{Exp(Re(y)), Exp(Im(y))}-Exp(Re(x/y))
234 k = exr - ezr + MPC_MAX(-eyr, eyr - 2 * eyi);
235 err = k < 2 ? 7 : (k == 2 ? 8 : (5 + k));
237 /* Can the real part be rounded ? */
238 ok = mpfr_inf_p (MPC_RE (x))
239 || mpfr_can_round (MPC_RE(x), prec - err, GMP_RNDN, GMP_RNDZ,
240 MPFR_PREC(MPC_RE(rop)) + (MPC_RND_RE(rnd) == GMP_RNDN));
244 /* Can the imaginary part be rounded ? */
245 ok = mpfr_inf_p (MPC_IM (x))
246 || mpfr_can_round (MPC_IM(x), prec - 6, GMP_RNDN, GMP_RNDZ,
247 MPFR_PREC(MPC_IM(rop)) + (MPC_RND_IM(rnd) == GMP_RNDN));
252 inex = mpc_set (rop, x, rnd);