1 /* mpc_asin -- arcsine of a complex number.
3 Copyright (C) 2009 Philippe Th\'eveny
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. */
24 extern int set_pi_over_2 (mpfr_ptr rop, int s, mpfr_rnd_t rnd);
27 mpc_asin (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd)
29 mp_prec_t p, p_re, p_im;
30 mp_rnd_t rnd_re, rnd_im;
35 if (mpfr_nan_p (MPC_RE (op)) || mpfr_nan_p (MPC_IM (op)))
37 if (mpfr_inf_p (MPC_RE (op)) || mpfr_inf_p (MPC_IM (op)))
39 mpfr_set_nan (MPC_RE (rop));
40 mpfr_set_inf (MPC_IM (rop), mpfr_signbit (MPC_IM (op)) ? -1 : +1);
42 else if (mpfr_zero_p (MPC_RE (op)))
44 mpfr_set (MPC_RE (rop), MPC_RE (op), GMP_RNDN);
45 mpfr_set_nan (MPC_IM (rop));
49 mpfr_set_nan (MPC_RE (rop));
50 mpfr_set_nan (MPC_IM (rop));
56 if (mpfr_inf_p (MPC_RE (op)) || mpfr_inf_p (MPC_IM (op)))
59 if (mpfr_inf_p (MPC_RE (op)))
61 inex_re = set_pi_over_2 (MPC_RE (rop), -mpfr_signbit (MPC_RE (op)),
63 mpfr_set_inf (MPC_IM (rop), -mpfr_signbit (MPC_IM (op)));
65 if (mpfr_inf_p (MPC_IM (op)))
66 mpfr_div_2ui (MPC_RE (rop), MPC_RE (rop), 1, GMP_RNDN);
71 s = mpfr_signbit (MPC_RE (op));
72 inex_re = mpfr_set_ui (MPC_RE (rop), 0, GMP_RNDN);
74 mpfr_neg (MPC_RE (rop), MPC_RE (rop), GMP_RNDN);
75 mpfr_set_inf (MPC_IM (rop), -mpfr_signbit (MPC_IM (op)));
78 return MPC_INEX (inex_re, 0);
81 /* pure real argument */
82 if (mpfr_zero_p (MPC_IM (op)))
87 s_im = mpfr_signbit (MPC_IM (op));
89 if (mpfr_cmp_ui (MPC_RE (op), 1) > 0)
92 inex_im = -mpfr_acosh (MPC_IM (rop), MPC_RE (op),
93 INV_RND (MPC_RND_IM (rnd)));
95 inex_im = mpfr_acosh (MPC_IM (rop), MPC_RE (op),
97 inex_re = set_pi_over_2 (MPC_RE (rop), -mpfr_signbit (MPC_RE (op)),
100 mpc_conj (rop, rop, MPC_RNDNN);
102 else if (mpfr_cmp_si (MPC_RE (op), -1) < 0)
105 minus_op_re[0] = MPC_RE (op)[0];
106 MPFR_CHANGE_SIGN (minus_op_re);
109 inex_im = -mpfr_acosh (MPC_IM (rop), minus_op_re,
110 INV_RND (MPC_RND_IM (rnd)));
112 inex_im = mpfr_acosh (MPC_IM (rop), minus_op_re,
114 inex_re = set_pi_over_2 (MPC_RE (rop), -mpfr_signbit (MPC_RE (op)),
117 mpc_conj (rop, rop, MPC_RNDNN);
121 inex_im = mpfr_set_ui (MPC_IM (rop), 0, MPC_RND_IM (rnd));
123 mpfr_neg (MPC_IM (rop), MPC_IM (rop), GMP_RNDN);
124 inex_re = mpfr_asin (MPC_RE (rop), MPC_RE (op), MPC_RND_RE (rnd));
127 return MPC_INEX (inex_re, inex_im);
130 /* pure imaginary argument */
131 if (mpfr_zero_p (MPC_RE (op)))
135 s = mpfr_signbit (MPC_RE (op));
136 mpfr_set_ui (MPC_RE (rop), 0, GMP_RNDN);
138 mpfr_neg (MPC_RE (rop), MPC_RE (rop), GMP_RNDN);
139 inex_im = mpfr_asinh (MPC_IM (rop), MPC_IM (op), MPC_RND_IM (rnd));
141 return MPC_INEX (0, inex_im);
144 /* regular complex: asin(z) = -i*log(i*z+sqrt(1-z^2)) */
145 p_re = mpfr_get_prec (MPC_RE(rop));
146 p_im = mpfr_get_prec (MPC_IM(rop));
147 rnd_re = MPC_RND_RE(rnd);
148 rnd_im = MPC_RND_IM(rnd);
149 p = p_re >= p_im ? p_re : p_im;
153 mp_exp_t ex, ey, err;
154 p += mpc_ceil_log2 (p) + 3;
155 mpfr_set_prec (MPC_RE(z1), p);
156 mpfr_set_prec (MPC_IM(z1), p);
159 mpc_sqr (z1, op, MPC_RNDNN);
160 /* err(x) <= 1/2 ulp(x), err(y) <= 1/2 ulp(y) */
162 ex = mpfr_get_exp (MPC_RE(z1));
163 mpfr_ui_sub (MPC_RE(z1), 1, MPC_RE(z1), GMP_RNDN);
164 mpfr_neg (MPC_IM(z1), MPC_IM(z1), GMP_RNDN);
165 ex = ex - mpfr_get_exp (MPC_RE(z1));
166 ex = (ex <= 0) ? 0 : ex;
167 /* err(x) <= 2^ex * ulp(x) */
168 ex = ex + mpfr_get_exp (MPC_RE(z1)) - p;
170 ey = mpfr_get_exp (MPC_IM(z1)) - p - 1;
172 ex = (ex >= ey) ? ex : ey; /* err(x), err(y) <= 2^ex, i.e., the norm
173 of the error is bounded by |h|<=2^(ex+1/2) */
174 /* z1 <- sqrt(z1): if z1 = z + h, then sqrt(z1) = sqrt(z) + h/2/sqrt(t) */
175 ey = mpfr_get_exp (MPC_RE(z1)) >= mpfr_get_exp (MPC_IM(z1))
176 ? mpfr_get_exp (MPC_RE(z1)) : mpfr_get_exp (MPC_IM(z1));
177 /* we have |z1| >= 2^(ey-1) thus 1/|z1| <= 2^(1-ey) */
178 mpc_sqrt (z1, z1, MPC_RNDNN);
179 ex = (2 * ex + 1) - 2 - (ey - 1); /* |h^2/4/|t| <= 2^ex */
180 ex = (ex + 1) / 2; /* ceil(ex/2) */
181 /* express ex in terms of ulp(z1) */
182 ey = mpfr_get_exp (MPC_RE(z1)) <= mpfr_get_exp (MPC_IM(z1))
183 ? mpfr_get_exp (MPC_RE(z1)) : mpfr_get_exp (MPC_IM(z1));
185 /* take into account the rounding error in the mpc_sqrt call */
186 err = (ex <= 0) ? 1 : ex + 1;
187 /* err(x) <= 2^err * ulp(x), err(y) <= 2^err * ulp(y) */
189 ex = mpfr_get_exp (MPC_RE(z1));
190 ey = mpfr_get_exp (MPC_IM(z1));
191 mpfr_sub (MPC_RE(z1), MPC_RE(z1), MPC_IM(op), GMP_RNDN);
192 mpfr_add (MPC_IM(z1), MPC_IM(z1), MPC_RE(op), GMP_RNDN);
193 if (mpfr_cmp_ui (MPC_RE(z1), 0) == 0 || mpfr_cmp_ui (MPC_IM(z1), 0) == 0)
195 ex -= mpfr_get_exp (MPC_RE(z1)); /* cancellation in x */
196 ey -= mpfr_get_exp (MPC_IM(z1)); /* cancellation in y */
197 ex = (ex >= ey) ? ex : ey; /* maximum cancellation */
199 err = (err <= 0) ? 1 : err + 1; /* rounding error in sub/add */
200 /* z1 <- log(z1): if z1 = z + h, then log(z1) = log(z) + h/t with
201 |t| >= min(|z1|,|z|) */
202 ex = mpfr_get_exp (MPC_RE(z1));
203 ey = mpfr_get_exp (MPC_IM(z1));
204 ex = (ex >= ey) ? ex : ey;
205 err += ex - p; /* revert to absolute error <= 2^err */
206 mpc_log (z1, z1, GMP_RNDN);
207 err -= ex - 1; /* 1/|t| <= 1/|z| <= 2^(1-ex) */
208 /* express err in terms of ulp(z1) */
209 ey = mpfr_get_exp (MPC_RE(z1)) <= mpfr_get_exp (MPC_IM(z1))
210 ? mpfr_get_exp (MPC_RE(z1)) : mpfr_get_exp (MPC_IM(z1));
212 /* take into account the rounding error in the mpc_log call */
213 err = (err <= 0) ? 1 : err + 1;
215 mpfr_swap (MPC_RE(z1), MPC_IM(z1));
216 mpfr_neg (MPC_IM(z1), MPC_IM(z1), GMP_RNDN);
217 if (mpfr_can_round (MPC_RE(z1), p - err, GMP_RNDN, GMP_RNDZ,
218 p_re + (rnd_re == GMP_RNDN)) &&
219 mpfr_can_round (MPC_IM(z1), p - err, GMP_RNDN, GMP_RNDZ,
220 p_im + (rnd_im == GMP_RNDN)))
224 inex = mpc_set (rop, z1, rnd);