1 /* mpc_sin_cos -- combined sine and cosine of a complex number.
3 Copyright (C) 2010, 2011 INRIA
5 This file is part of GNU MPC.
7 GNU MPC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU Lesser General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
12 GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
17 You should have received a copy of the GNU Lesser General Public License
18 along with this program. If not, see http://www.gnu.org/licenses/ .
24 mpc_sin_cos_nonfinite (mpc_ptr rop_sin, mpc_ptr rop_cos, mpc_srcptr op,
25 mpc_rnd_t rnd_sin, mpc_rnd_t rnd_cos)
26 /* assumes that op (that is, its real or imaginary part) is not finite */
31 overlap = (rop_sin == op || rop_cos == op);
33 mpc_init3 (op_loc, MPC_PREC_RE (op), MPC_PREC_IM (op));
34 mpc_set (op_loc, op, MPC_RNDNN);
39 if (rop_sin != NULL) {
40 if (mpfr_nan_p (mpc_realref (op_loc)) || mpfr_nan_p (mpc_imagref (op_loc))) {
41 mpc_set (rop_sin, op_loc, rnd_sin);
42 if (mpfr_nan_p (mpc_imagref (op_loc))) {
43 /* sin(x +i*NaN) = NaN +i*NaN, except for x=0 */
44 /* sin(-0 +i*NaN) = -0 +i*NaN */
45 /* sin(+0 +i*NaN) = +0 +i*NaN */
46 if (!mpfr_zero_p (mpc_realref (op_loc)))
47 mpfr_set_nan (mpc_realref (rop_sin));
49 else /* op = NaN + i*y */
50 if (!mpfr_inf_p (mpc_imagref (op_loc)) && !mpfr_zero_p (mpc_imagref (op_loc)))
51 /* sin(NaN -i*Inf) = NaN -i*Inf */
52 /* sin(NaN -i*0) = NaN -i*0 */
53 /* sin(NaN +i*0) = NaN +i*0 */
54 /* sin(NaN +i*Inf) = NaN +i*Inf */
55 /* sin(NaN +i*y) = NaN +i*NaN, when 0<|y|<Inf */
56 mpfr_set_nan (mpc_imagref (rop_sin));
58 else if (mpfr_inf_p (mpc_realref (op_loc))) {
59 mpfr_set_nan (mpc_realref (rop_sin));
61 if (!mpfr_inf_p (mpc_imagref (op_loc)) && !mpfr_zero_p (mpc_imagref (op_loc)))
62 /* sin(+/-Inf +i*y) = NaN +i*NaN, when 0<|y|<Inf */
63 mpfr_set_nan (mpc_imagref (rop_sin));
65 /* sin(+/-Inf -i*Inf) = NaN -i*Inf */
66 /* sin(+/-Inf +i*Inf) = NaN +i*Inf */
67 /* sin(+/-Inf -i*0) = NaN -i*0 */
68 /* sin(+/-Inf +i*0) = NaN +i*0 */
69 mpfr_set (mpc_imagref (rop_sin), mpc_imagref (op_loc), MPC_RND_IM (rnd_sin));
71 else if (mpfr_zero_p (mpc_realref (op_loc))) {
72 /* sin(-0 -i*Inf) = -0 -i*Inf */
73 /* sin(+0 -i*Inf) = +0 -i*Inf */
74 /* sin(-0 +i*Inf) = -0 +i*Inf */
75 /* sin(+0 +i*Inf) = +0 +i*Inf */
76 mpc_set (rop_sin, op_loc, rnd_sin);
79 /* sin(x -i*Inf) = +Inf*(sin(x) -i*cos(x)) */
80 /* sin(x +i*Inf) = +Inf*(sin(x) +i*cos(x)) */
84 mpfr_sin_cos (s, c, mpc_realref (op_loc), GMP_RNDZ);
85 mpfr_set_inf (mpc_realref (rop_sin), MPFR_SIGN (s));
86 mpfr_set_inf (mpc_imagref (rop_sin), MPFR_SIGN (c)*MPFR_SIGN (mpc_imagref (op_loc)));
92 if (rop_cos != NULL) {
93 if (mpfr_nan_p (mpc_realref (op_loc))) {
94 /* cos(NaN + i * NaN) = NaN + i * NaN */
95 /* cos(NaN - i * Inf) = +Inf + i * NaN */
96 /* cos(NaN + i * Inf) = +Inf + i * NaN */
97 /* cos(NaN - i * 0) = NaN - i * 0 */
98 /* cos(NaN + i * 0) = NaN + i * 0 */
99 /* cos(NaN + i * y) = NaN + i * NaN, when y != 0 */
100 if (mpfr_inf_p (mpc_imagref (op_loc)))
101 mpfr_set_inf (mpc_realref (rop_cos), +1);
103 mpfr_set_nan (mpc_realref (rop_cos));
105 if (mpfr_zero_p (mpc_imagref (op_loc)))
106 mpfr_set (mpc_imagref (rop_cos), mpc_imagref (op_loc), MPC_RND_IM (rnd_cos));
108 mpfr_set_nan (mpc_imagref (rop_cos));
110 else if (mpfr_nan_p (mpc_imagref (op_loc))) {
111 /* cos(-Inf + i * NaN) = NaN + i * NaN */
112 /* cos(+Inf + i * NaN) = NaN + i * NaN */
113 /* cos(-0 + i * NaN) = NaN - i * 0 */
114 /* cos(+0 + i * NaN) = NaN + i * 0 */
115 /* cos(x + i * NaN) = NaN + i * NaN, when x != 0 */
116 if (mpfr_zero_p (mpc_realref (op_loc)))
117 mpfr_set (mpc_imagref (rop_cos), mpc_realref (op_loc), MPC_RND_IM (rnd_cos));
119 mpfr_set_nan (mpc_imagref (rop_cos));
121 mpfr_set_nan (mpc_realref (rop_cos));
123 else if (mpfr_inf_p (mpc_realref (op_loc))) {
124 /* cos(-Inf -i*Inf) = cos(+Inf +i*Inf) = -Inf +i*NaN */
125 /* cos(-Inf +i*Inf) = cos(+Inf -i*Inf) = +Inf +i*NaN */
126 /* cos(-Inf -i*0) = cos(+Inf +i*0) = NaN -i*0 */
127 /* cos(-Inf +i*0) = cos(+Inf -i*0) = NaN +i*0 */
128 /* cos(-Inf +i*y) = cos(+Inf +i*y) = NaN +i*NaN, when y != 0 */
130 const int same_sign =
131 mpfr_signbit (mpc_realref (op_loc)) == mpfr_signbit (mpc_imagref (op_loc));
133 if (mpfr_inf_p (mpc_imagref (op_loc)))
134 mpfr_set_inf (mpc_realref (rop_cos), (same_sign ? -1 : +1));
136 mpfr_set_nan (mpc_realref (rop_cos));
138 if (mpfr_zero_p (mpc_imagref (op_loc)))
139 mpfr_setsign (mpc_imagref (rop_cos), mpc_imagref (op_loc), same_sign,
140 MPC_RND_IM(rnd_cos));
142 mpfr_set_nan (mpc_imagref (rop_cos));
144 else if (mpfr_zero_p (mpc_realref (op_loc))) {
145 /* cos(-0 -i*Inf) = cos(+0 +i*Inf) = +Inf -i*0 */
146 /* cos(-0 +i*Inf) = cos(+0 -i*Inf) = +Inf +i*0 */
147 const int same_sign =
148 mpfr_signbit (mpc_realref (op_loc)) == mpfr_signbit (mpc_imagref (op_loc));
150 mpfr_setsign (mpc_imagref (rop_cos), mpc_realref (op_loc), same_sign,
151 MPC_RND_IM (rnd_cos));
152 mpfr_set_inf (mpc_realref (rop_cos), +1);
155 /* cos(x -i*Inf) = +Inf*cos(x) +i*Inf*sin(x), when x != 0 */
156 /* cos(x +i*Inf) = +Inf*cos(x) -i*Inf*sin(x), when x != 0 */
160 mpfr_sin_cos (s, c, mpc_realref (op_loc), GMP_RNDN);
161 mpfr_set_inf (mpc_realref (rop_cos), mpfr_sgn (c));
162 mpfr_set_inf (mpc_imagref (rop_cos),
163 (mpfr_sgn (mpc_imagref (op_loc)) == mpfr_sgn (s) ? -1 : +1));
172 return MPC_INEX12 (MPC_INEX (0,0), MPC_INEX (0,0));
173 /* everything is exact */
178 mpc_sin_cos_real (mpc_ptr rop_sin, mpc_ptr rop_cos, mpc_srcptr op,
179 mpc_rnd_t rnd_sin, mpc_rnd_t rnd_cos)
180 /* assumes that op is real */
182 int inex_sin_re = 0, inex_cos_re = 0;
183 /* Until further notice, assume computations exact; in particular,
184 by definition, for not computed values. */
187 int sign_im = mpfr_signbit (mpc_imagref (op));
189 /* sin(x +-0*i) = sin(x) +-0*i*sign(cos(x)) */
190 /* cos(x +-i*0) = cos(x) -+i*0*sign(sin(x)) */
192 mpfr_init2 (s, MPC_PREC_RE (rop_sin));
194 mpfr_init2 (s, 2); /* We need only the sign. */
196 mpfr_init2 (c, MPC_PREC_RE (rop_cos));
199 inex_s = mpfr_sin (s, mpc_realref (op), MPC_RND_RE (rnd_sin));
200 inex_c = mpfr_cos (c, mpc_realref (op), MPC_RND_RE (rnd_cos));
201 /* We cannot use mpfr_sin_cos since we may need two distinct rounding
202 modes and the exact return values. If we need only the sign, an
203 arbitrary rounding mode will work. */
205 if (rop_sin != NULL) {
206 mpfr_set (mpc_realref (rop_sin), s, GMP_RNDN); /* exact */
207 inex_sin_re = inex_s;
208 mpfr_set_zero (mpc_imagref (rop_sin),
209 ( ( sign_im && !mpfr_signbit(c))
210 || (!sign_im && mpfr_signbit(c)) ? -1 : 1));
213 if (rop_cos != NULL) {
214 mpfr_set (mpc_realref (rop_cos), c, GMP_RNDN); /* exact */
215 inex_cos_re = inex_c;
216 mpfr_set_zero (mpc_imagref (rop_cos),
217 ( ( sign_im && mpfr_signbit(s))
218 || (!sign_im && !mpfr_signbit(s)) ? -1 : 1));
224 return MPC_INEX12 (MPC_INEX (inex_sin_re, 0), MPC_INEX (inex_cos_re, 0));
229 mpc_sin_cos_imag (mpc_ptr rop_sin, mpc_ptr rop_cos, mpc_srcptr op,
230 mpc_rnd_t rnd_sin, mpc_rnd_t rnd_cos)
231 /* assumes that op is purely imaginary, but not zero */
233 int inex_sin_im = 0, inex_cos_re = 0;
234 /* assume exact if not computed */
238 overlap = (rop_sin == op || rop_cos == op);
240 mpc_init3 (op_loc, MPC_PREC_RE (op), MPC_PREC_IM (op));
241 mpc_set (op_loc, op, MPC_RNDNN);
246 if (rop_sin != NULL) {
247 /* sin(+-O +i*y) = +-0 +i*sinh(y) */
248 mpfr_set (mpc_realref(rop_sin), mpc_realref(op_loc), GMP_RNDN);
249 inex_sin_im = mpfr_sinh (mpc_imagref(rop_sin), mpc_imagref(op_loc), MPC_RND_IM(rnd_sin));
252 if (rop_cos != NULL) {
253 /* cos(-0 - i * y) = cos(+0 + i * y) = cosh(y) - i * 0,
254 cos(-0 + i * y) = cos(+0 - i * y) = cosh(y) + i * 0,
256 inex_cos_re = mpfr_cosh (mpc_realref (rop_cos), mpc_imagref (op_loc), MPC_RND_RE (rnd_cos));
258 mpfr_set_ui (mpc_imagref (rop_cos), 0ul, MPC_RND_IM (rnd_cos));
259 if (mpfr_signbit (mpc_realref (op_loc)) == mpfr_signbit (mpc_imagref (op_loc)))
260 MPFR_CHANGE_SIGN (mpc_imagref (rop_cos));
266 return MPC_INEX12 (MPC_INEX (0, inex_sin_im), MPC_INEX (inex_cos_re, 0));
271 mpc_sin_cos (mpc_ptr rop_sin, mpc_ptr rop_cos, mpc_srcptr op,
272 mpc_rnd_t rnd_sin, mpc_rnd_t rnd_cos)
273 /* Feature not documented in the texinfo file: One of rop_sin or
274 rop_cos may be NULL, in which case it is not computed, and the
275 corresponding ternary inexact value is set to 0 (exact). */
278 return mpc_sin_cos_nonfinite (rop_sin, rop_cos, op, rnd_sin, rnd_cos);
279 else if (mpfr_zero_p (mpc_imagref (op)))
280 return mpc_sin_cos_real (rop_sin, rop_cos, op, rnd_sin, rnd_cos);
281 else if (mpfr_zero_p (mpc_realref (op)))
282 return mpc_sin_cos_imag (rop_sin, rop_cos, op, rnd_sin, rnd_cos);
284 /* let op = a + i*b, then sin(op) = sin(a)*cosh(b) + i*cos(a)*sinh(b)
285 and cos(op) = cos(a)*cosh(b) - i*sin(a)*sinh(b).
287 For Re(sin(op)) (and analogously, the other parts), we use the
288 following algorithm, with rounding to nearest for all operations
289 and working precision w:
294 then the error on r is at most 4 ulps, since we can write
295 r = sin(a)*cosh(b)*(1+t)^3 with |t| <= 2^(-w),
296 thus for w >= 2, r = sin(a)*cosh(b)*(1+4*t) with |t| <= 2^(-w),
297 thus the relative error is bounded by 4*2^(-w) <= 4*ulp(r).
299 mpfr_t s, c, sh, ch, sch, csh;
302 int inex_re, inex_im, inex_sin, inex_cos;
306 prec = MPC_MAX (prec, MPC_MAX_PREC (rop_sin));
308 prec = MPC_MAX (prec, MPC_MAX_PREC (rop_cos));
319 prec += mpc_ceil_log2 (prec) + 5;
321 mpfr_set_prec (s, prec);
322 mpfr_set_prec (c, prec);
323 mpfr_set_prec (sh, prec);
324 mpfr_set_prec (ch, prec);
325 mpfr_set_prec (sch, prec);
326 mpfr_set_prec (csh, prec);
328 mpfr_sin_cos (s, c, mpc_realref(op), GMP_RNDN);
329 mpfr_sinh_cosh (sh, ch, mpc_imagref(op), GMP_RNDN);
331 if (rop_sin != NULL) {
332 /* real part of sine */
333 mpfr_mul (sch, s, ch, GMP_RNDN);
334 ok = (!mpfr_number_p (sch))
335 || mpfr_can_round (sch, prec - 2, GMP_RNDN, GMP_RNDZ,
336 MPC_PREC_RE (rop_sin)
337 + (MPC_RND_RE (rnd_sin) == GMP_RNDN));
340 /* imaginary part of sine */
341 mpfr_mul (csh, c, sh, GMP_RNDN);
342 ok = (!mpfr_number_p (csh))
343 || mpfr_can_round (csh, prec - 2, GMP_RNDN, GMP_RNDZ,
344 MPC_PREC_IM (rop_sin)
345 + (MPC_RND_IM (rnd_sin) == GMP_RNDN));
349 if (rop_cos != NULL && ok) {
350 /* real part of cosine */
351 mpfr_mul (c, c, ch, GMP_RNDN);
352 ok = (!mpfr_number_p (c))
353 || mpfr_can_round (c, prec - 2, GMP_RNDN, GMP_RNDZ,
354 MPC_PREC_RE (rop_cos)
355 + (MPC_RND_RE (rnd_cos) == GMP_RNDN));
358 /* imaginary part of cosine */
359 mpfr_mul (s, s, sh, GMP_RNDN);
360 mpfr_neg (s, s, GMP_RNDN);
361 ok = (!mpfr_number_p (s))
362 || mpfr_can_round (s, prec - 2, GMP_RNDN, GMP_RNDZ,
363 MPC_PREC_IM (rop_cos)
364 + (MPC_RND_IM (rnd_cos) == GMP_RNDN));
369 if (rop_sin != NULL) {
370 inex_re = mpfr_set (mpc_realref (rop_sin), sch, MPC_RND_RE (rnd_sin));
371 if (mpfr_inf_p (sch))
372 inex_re = mpfr_sgn (sch);
373 inex_im = mpfr_set (mpc_imagref (rop_sin), csh, MPC_RND_IM (rnd_sin));
374 if (mpfr_inf_p (csh))
375 inex_im = mpfr_sgn (csh);
376 inex_sin = MPC_INEX (inex_re, inex_im);
379 inex_sin = MPC_INEX (0,0); /* return exact if not computed */
381 if (rop_cos != NULL) {
382 inex_re = mpfr_set (mpc_realref (rop_cos), c, MPC_RND_RE (rnd_cos));
384 inex_re = mpfr_sgn (c);
385 inex_im = mpfr_set (mpc_imagref (rop_cos), s, MPC_RND_IM (rnd_cos));
387 inex_im = mpfr_sgn (s);
388 inex_cos = MPC_INEX (inex_re, inex_im);
391 inex_cos = MPC_INEX (0,0); /* return exact if not computed */
400 return (MPC_INEX12 (inex_sin, inex_cos));