1
/* mpc_acos -- arccosine of a complex number.
3
Copyright (C) INRIA, 2009, 2010
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. */
22
#include <stdio.h> /* for MPC_ASSERT */
26
mpc_acos (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd)
28
int inex_re, inex_im, inex;
29
mpfr_prec_t p_re, p_im, p;
40
if (mpfr_nan_p (MPC_RE (op)) || mpfr_nan_p (MPC_IM (op)))
42
if (mpfr_inf_p (MPC_RE (op)) || mpfr_inf_p (MPC_IM (op)))
44
mpfr_set_inf (MPC_IM (rop), mpfr_signbit (MPC_IM (op)) ? +1 : -1);
45
mpfr_set_nan (MPC_RE (rop));
47
else if (mpfr_zero_p (MPC_RE (op)))
49
inex_re = set_pi_over_2 (MPC_RE (rop), +1, MPC_RND_RE (rnd));
50
mpfr_set_nan (MPC_IM (rop));
54
mpfr_set_nan (MPC_RE (rop));
55
mpfr_set_nan (MPC_IM (rop));
58
return MPC_INEX (inex_re, 0);
61
if (mpfr_inf_p (MPC_RE (op)) || mpfr_inf_p (MPC_IM (op)))
63
if (mpfr_inf_p (MPC_RE (op)))
65
if (mpfr_inf_p (MPC_IM (op)))
67
if (mpfr_sgn (MPC_RE (op)) > 0)
70
set_pi_over_2 (MPC_RE (rop), +1, MPC_RND_RE (rnd));
71
mpfr_div_2ui (MPC_RE (rop), MPC_RE (rop), 1, GMP_RNDN);
76
/* the real part of the result is 3*pi/4
77
a = o(pi) error(a) < 1 ulp(a)
78
b = o(3*a) error(b) < 2 ulp(b)
85
prec = mpfr_get_prec (MPC_RE (rop));
90
p += mpc_ceil_log2 (p);
92
mpfr_const_pi (x, GMP_RNDD);
93
mpfr_mul_ui (x, x, 3, GMP_RNDD);
95
mpfr_can_round (x, p - 1, GMP_RNDD, MPC_RND_RE (rnd),
96
prec+(MPC_RND_RE (rnd) == GMP_RNDN));
100
mpfr_div_2ui (MPC_RE (rop), x, 2, MPC_RND_RE (rnd));
106
if (mpfr_sgn (MPC_RE (op)) > 0)
107
mpfr_set_ui (MPC_RE (rop), 0, GMP_RNDN);
109
inex_re = mpfr_const_pi (MPC_RE (rop), MPC_RND_RE (rnd));
113
inex_re = set_pi_over_2 (MPC_RE (rop), +1, MPC_RND_RE (rnd));
115
mpfr_set_inf (MPC_IM (rop), mpfr_signbit (MPC_IM (op)) ? +1 : -1);
117
return MPC_INEX (inex_re, 0);
120
/* pure real argument */
121
if (mpfr_zero_p (MPC_IM (op)))
124
s_im = mpfr_signbit (MPC_IM (op));
126
if (mpfr_cmp_ui (MPC_RE (op), 1) > 0)
129
inex_im = mpfr_acosh (MPC_IM (rop), MPC_RE (op),
132
inex_im = -mpfr_acosh (MPC_IM (rop), MPC_RE (op),
133
INV_RND (MPC_RND_IM (rnd)));
135
mpfr_set_ui (MPC_RE (rop), 0, GMP_RNDN);
137
else if (mpfr_cmp_si (MPC_RE (op), -1) < 0)
140
minus_op_re[0] = MPC_RE (op)[0];
141
MPFR_CHANGE_SIGN (minus_op_re);
144
inex_im = mpfr_acosh (MPC_IM (rop), minus_op_re,
147
inex_im = -mpfr_acosh (MPC_IM (rop), minus_op_re,
148
INV_RND (MPC_RND_IM (rnd)));
149
inex_re = mpfr_const_pi (MPC_RE (rop), MPC_RND_RE (rnd));
153
inex_re = mpfr_acos (MPC_RE (rop), MPC_RE (op), MPC_RND_RE (rnd));
154
mpfr_set_ui (MPC_IM (rop), 0, MPC_RND_IM (rnd));
158
mpc_conj (rop, rop, MPC_RNDNN);
160
return MPC_INEX (inex_re, inex_im);
163
/* pure imaginary argument */
164
if (mpfr_zero_p (MPC_RE (op)))
166
inex_re = set_pi_over_2 (MPC_RE (rop), +1, MPC_RND_RE (rnd));
167
inex_im = -mpfr_asinh (MPC_IM (rop), MPC_IM (op),
168
INV_RND (MPC_RND_IM (rnd)));
169
mpc_conj (rop,rop, MPC_RNDNN);
171
return MPC_INEX (inex_re, inex_im);
174
/* regular complex argument: acos(z) = Pi/2 - asin(z) */
175
p_re = mpfr_get_prec (MPC_RE(rop));
176
p_im = mpfr_get_prec (MPC_IM(rop));
178
mpc_init3 (z1, p, p_im); /* we round directly the imaginary part to p_im,
179
with rounding mode opposite to rnd_im */
180
rnd_im = MPC_RND_IM(rnd);
181
/* the imaginary part of asin(z) has the same sign as Im(z), thus if
182
Im(z) > 0 and rnd_im = RNDZ, we want to round the Im(asin(z)) to -Inf
183
so that -Im(asin(z)) is rounded to zero */
184
if (rnd_im == GMP_RNDZ)
185
rnd_im = mpfr_sgn (MPC_IM(op)) > 0 ? GMP_RNDD : GMP_RNDU;
187
rnd_im = rnd_im == GMP_RNDU ? GMP_RNDD
188
: rnd_im == GMP_RNDD ? GMP_RNDU
189
: rnd_im; /* both RNDZ and RNDA map to themselves for -asin(z) */
190
rnd1 = RNDC(GMP_RNDN, rnd_im);
191
mpfr_init2 (pi_over_2, p);
194
p += mpc_ceil_log2 (p) + 3;
196
mpfr_set_prec (MPC_RE(z1), p);
197
mpfr_set_prec (pi_over_2, p);
199
mpfr_const_pi (pi_over_2, GMP_RNDN);
200
mpfr_div_2exp (pi_over_2, pi_over_2, 1, GMP_RNDN); /* Pi/2 */
201
e1 = 1; /* Exp(pi_over_2) */
202
inex = mpc_asin (z1, op, rnd1); /* asin(z) */
203
MPC_ASSERT (mpfr_sgn (MPC_IM(z1)) * mpfr_sgn (MPC_IM(op)) > 0);
204
inex_im = MPC_INEX_IM(inex); /* inex_im is in {-1, 0, 1} */
205
e2 = mpfr_get_exp (MPC_RE(z1));
206
mpfr_sub (MPC_RE(z1), pi_over_2, MPC_RE(z1), GMP_RNDN);
207
if (!mpfr_zero_p (MPC_RE(z1)))
209
/* the error on x=Re(z1) is bounded by 1/2 ulp(x) + 2^(e1-p-1) +
211
e1 = e1 >= e2 ? e1 + 1 : e2 + 1;
212
/* the error on x is bounded by 1/2 ulp(x) + 2^(e1-p-1) */
213
e1 -= mpfr_get_exp (MPC_RE(z1));
214
/* the error on x is bounded by 1/2 ulp(x) [1 + 2^e1] */
215
e1 = e1 <= 0 ? 0 : e1;
216
/* the error on x is bounded by 2^e1 * ulp(x) */
217
mpfr_neg (MPC_IM(z1), MPC_IM(z1), GMP_RNDN); /* exact */
219
if (mpfr_can_round (MPC_RE(z1), p - e1, GMP_RNDN, GMP_RNDZ,
220
p_re + (MPC_RND_RE(rnd) == GMP_RNDN)))
224
inex = mpc_set (rop, z1, rnd);
225
inex_re = MPC_INEX_RE(inex);
227
mpfr_clear (pi_over_2);
229
return MPC_INEX(inex_re, inex_im);