xref: /dflybsd-src/contrib/mpc/src/log.c (revision 04febcfb30580676d3e95f58a16c5137ee478b32)
1*d30dc8cbSJohn Marino /* mpc_log -- Take the logarithm of a complex number.
2*d30dc8cbSJohn Marino 
3*d30dc8cbSJohn Marino Copyright (C) 2008, 2009, 2010, 2011, 2012 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 "mpc-impl.h"
23*d30dc8cbSJohn Marino 
24*d30dc8cbSJohn Marino int
mpc_log(mpc_ptr rop,mpc_srcptr op,mpc_rnd_t rnd)25*d30dc8cbSJohn Marino mpc_log (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd){
26*d30dc8cbSJohn Marino    int ok, underflow = 0;
27*d30dc8cbSJohn Marino    mpfr_srcptr x, y;
28*d30dc8cbSJohn Marino    mpfr_t v, w;
29*d30dc8cbSJohn Marino    mpfr_prec_t prec;
30*d30dc8cbSJohn Marino    int loops;
31*d30dc8cbSJohn Marino    int re_cmp, im_cmp;
32*d30dc8cbSJohn Marino    int inex_re, inex_im;
33*d30dc8cbSJohn Marino    int err;
34*d30dc8cbSJohn Marino    mpfr_exp_t expw;
35*d30dc8cbSJohn Marino    int sgnw;
36*d30dc8cbSJohn Marino 
37*d30dc8cbSJohn Marino    /* special values: NaN and infinities */
38*d30dc8cbSJohn Marino    if (!mpc_fin_p (op)) {
39*d30dc8cbSJohn Marino       if (mpfr_nan_p (mpc_realref (op))) {
40*d30dc8cbSJohn Marino          if (mpfr_inf_p (mpc_imagref (op)))
41*d30dc8cbSJohn Marino             mpfr_set_inf (mpc_realref (rop), +1);
42*d30dc8cbSJohn Marino          else
43*d30dc8cbSJohn Marino             mpfr_set_nan (mpc_realref (rop));
44*d30dc8cbSJohn Marino          mpfr_set_nan (mpc_imagref (rop));
45*d30dc8cbSJohn Marino          inex_im = 0; /* Inf/NaN is exact */
46*d30dc8cbSJohn Marino       }
47*d30dc8cbSJohn Marino       else if (mpfr_nan_p (mpc_imagref (op))) {
48*d30dc8cbSJohn Marino          if (mpfr_inf_p (mpc_realref (op)))
49*d30dc8cbSJohn Marino             mpfr_set_inf (mpc_realref (rop), +1);
50*d30dc8cbSJohn Marino          else
51*d30dc8cbSJohn Marino             mpfr_set_nan (mpc_realref (rop));
52*d30dc8cbSJohn Marino          mpfr_set_nan (mpc_imagref (rop));
53*d30dc8cbSJohn Marino          inex_im = 0; /* Inf/NaN is exact */
54*d30dc8cbSJohn Marino       }
55*d30dc8cbSJohn Marino       else /* We have an infinity in at least one part. */ {
56*d30dc8cbSJohn Marino          inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op),
57*d30dc8cbSJohn Marino                                MPC_RND_IM (rnd));
58*d30dc8cbSJohn Marino          mpfr_set_inf (mpc_realref (rop), +1);
59*d30dc8cbSJohn Marino       }
60*d30dc8cbSJohn Marino       return MPC_INEX(0, inex_im);
61*d30dc8cbSJohn Marino    }
62*d30dc8cbSJohn Marino 
63*d30dc8cbSJohn Marino    /* special cases: real and purely imaginary numbers */
64*d30dc8cbSJohn Marino    re_cmp = mpfr_cmp_ui (mpc_realref (op), 0);
65*d30dc8cbSJohn Marino    im_cmp = mpfr_cmp_ui (mpc_imagref (op), 0);
66*d30dc8cbSJohn Marino    if (im_cmp == 0) {
67*d30dc8cbSJohn Marino       if (re_cmp == 0) {
68*d30dc8cbSJohn Marino          inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op),
69*d30dc8cbSJohn Marino                                MPC_RND_IM (rnd));
70*d30dc8cbSJohn Marino          mpfr_set_inf (mpc_realref (rop), -1);
71*d30dc8cbSJohn Marino          inex_re = 0; /* -Inf is exact */
72*d30dc8cbSJohn Marino       }
73*d30dc8cbSJohn Marino       else if (re_cmp > 0) {
74*d30dc8cbSJohn Marino          inex_re = mpfr_log (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd));
75*d30dc8cbSJohn Marino          inex_im = mpfr_set (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd));
76*d30dc8cbSJohn Marino       }
77*d30dc8cbSJohn Marino       else {
78*d30dc8cbSJohn Marino          /* op = x + 0*y; let w = -x = |x| */
79*d30dc8cbSJohn Marino          int negative_zero;
80*d30dc8cbSJohn Marino          mpfr_rnd_t rnd_im;
81*d30dc8cbSJohn Marino 
82*d30dc8cbSJohn Marino          negative_zero = mpfr_signbit (mpc_imagref (op));
83*d30dc8cbSJohn Marino          if (negative_zero)
84*d30dc8cbSJohn Marino             rnd_im = INV_RND (MPC_RND_IM (rnd));
85*d30dc8cbSJohn Marino          else
86*d30dc8cbSJohn Marino             rnd_im = MPC_RND_IM (rnd);
87*d30dc8cbSJohn Marino          w [0] = *mpc_realref (op);
88*d30dc8cbSJohn Marino          MPFR_CHANGE_SIGN (w);
89*d30dc8cbSJohn Marino          inex_re = mpfr_log (mpc_realref (rop), w, MPC_RND_RE (rnd));
90*d30dc8cbSJohn Marino          inex_im = mpfr_const_pi (mpc_imagref (rop), rnd_im);
91*d30dc8cbSJohn Marino          if (negative_zero) {
92*d30dc8cbSJohn Marino             mpc_conj (rop, rop, MPC_RNDNN);
93*d30dc8cbSJohn Marino             inex_im = -inex_im;
94*d30dc8cbSJohn Marino          }
95*d30dc8cbSJohn Marino       }
96*d30dc8cbSJohn Marino       return MPC_INEX(inex_re, inex_im);
97*d30dc8cbSJohn Marino    }
98*d30dc8cbSJohn Marino    else if (re_cmp == 0) {
99*d30dc8cbSJohn Marino       if (im_cmp > 0) {
100*d30dc8cbSJohn Marino          inex_re = mpfr_log (mpc_realref (rop), mpc_imagref (op), MPC_RND_RE (rnd));
101*d30dc8cbSJohn Marino          inex_im = mpfr_const_pi (mpc_imagref (rop), MPC_RND_IM (rnd));
102*d30dc8cbSJohn Marino          /* division by 2 does not change the ternary flag */
103*d30dc8cbSJohn Marino          mpfr_div_2ui (mpc_imagref (rop), mpc_imagref (rop), 1, GMP_RNDN);
104*d30dc8cbSJohn Marino       }
105*d30dc8cbSJohn Marino       else {
106*d30dc8cbSJohn Marino          w [0] = *mpc_imagref (op);
107*d30dc8cbSJohn Marino          MPFR_CHANGE_SIGN (w);
108*d30dc8cbSJohn Marino          inex_re = mpfr_log (mpc_realref (rop), w, MPC_RND_RE (rnd));
109*d30dc8cbSJohn Marino          inex_im = mpfr_const_pi (mpc_imagref (rop), INV_RND (MPC_RND_IM (rnd)));
110*d30dc8cbSJohn Marino          /* division by 2 does not change the ternary flag */
111*d30dc8cbSJohn Marino          mpfr_div_2ui (mpc_imagref (rop), mpc_imagref (rop), 1, GMP_RNDN);
112*d30dc8cbSJohn Marino          mpfr_neg (mpc_imagref (rop), mpc_imagref (rop), GMP_RNDN);
113*d30dc8cbSJohn Marino          inex_im = -inex_im; /* negate the ternary flag */
114*d30dc8cbSJohn Marino       }
115*d30dc8cbSJohn Marino       return MPC_INEX(inex_re, inex_im);
116*d30dc8cbSJohn Marino    }
117*d30dc8cbSJohn Marino 
118*d30dc8cbSJohn Marino    prec = MPC_PREC_RE(rop);
119*d30dc8cbSJohn Marino    mpfr_init2 (w, 2);
120*d30dc8cbSJohn Marino    /* let op = x + iy; log = 1/2 log (x^2 + y^2) + i atan2 (y, x)   */
121*d30dc8cbSJohn Marino    /* loop for the real part: 1/2 log (x^2 + y^2), fast, but unsafe */
122*d30dc8cbSJohn Marino    /* implementation                                                */
123*d30dc8cbSJohn Marino    ok = 0;
124*d30dc8cbSJohn Marino    for (loops = 1; !ok && loops <= 2; loops++) {
125*d30dc8cbSJohn Marino       prec += mpc_ceil_log2 (prec) + 4;
126*d30dc8cbSJohn Marino       mpfr_set_prec (w, prec);
127*d30dc8cbSJohn Marino 
128*d30dc8cbSJohn Marino       mpc_abs (w, op, GMP_RNDN);
129*d30dc8cbSJohn Marino          /* error 0.5 ulp */
130*d30dc8cbSJohn Marino       if (mpfr_inf_p (w))
131*d30dc8cbSJohn Marino          /* intermediate overflow; the logarithm may be representable.
132*d30dc8cbSJohn Marino             Intermediate underflow is impossible.                      */
133*d30dc8cbSJohn Marino          break;
134*d30dc8cbSJohn Marino 
135*d30dc8cbSJohn Marino       mpfr_log (w, w, GMP_RNDN);
136*d30dc8cbSJohn Marino          /* generic error of log: (2^(- exp(w)) + 0.5) ulp */
137*d30dc8cbSJohn Marino 
138*d30dc8cbSJohn Marino       if (mpfr_zero_p (w))
139*d30dc8cbSJohn Marino          /* impossible to round, switch to second algorithm */
140*d30dc8cbSJohn Marino          break;
141*d30dc8cbSJohn Marino 
142*d30dc8cbSJohn Marino       err = MPC_MAX (-mpfr_get_exp (w), 0) + 1;
143*d30dc8cbSJohn Marino          /* number of lost digits */
144*d30dc8cbSJohn Marino       ok = mpfr_can_round (w, prec - err, GMP_RNDN, GMP_RNDZ,
145*d30dc8cbSJohn Marino          mpfr_get_prec (mpc_realref (rop)) + (MPC_RND_RE (rnd) == GMP_RNDN));
146*d30dc8cbSJohn Marino    }
147*d30dc8cbSJohn Marino 
148*d30dc8cbSJohn Marino    if (!ok) {
149*d30dc8cbSJohn Marino       prec = MPC_PREC_RE(rop);
150*d30dc8cbSJohn Marino       mpfr_init2 (v, 2);
151*d30dc8cbSJohn Marino       /* compute 1/2 log (x^2 + y^2) = log |x| + 1/2 * log (1 + (y/x)^2)
152*d30dc8cbSJohn Marino             if |x| >= |y|; otherwise, exchange x and y                   */
153*d30dc8cbSJohn Marino       if (mpfr_cmpabs (mpc_realref (op), mpc_imagref (op)) >= 0) {
154*d30dc8cbSJohn Marino          x = mpc_realref (op);
155*d30dc8cbSJohn Marino          y = mpc_imagref (op);
156*d30dc8cbSJohn Marino       }
157*d30dc8cbSJohn Marino       else {
158*d30dc8cbSJohn Marino          x = mpc_imagref (op);
159*d30dc8cbSJohn Marino          y = mpc_realref (op);
160*d30dc8cbSJohn Marino       }
161*d30dc8cbSJohn Marino 
162*d30dc8cbSJohn Marino       do {
163*d30dc8cbSJohn Marino          prec += mpc_ceil_log2 (prec) + 4;
164*d30dc8cbSJohn Marino          mpfr_set_prec (v, prec);
165*d30dc8cbSJohn Marino          mpfr_set_prec (w, prec);
166*d30dc8cbSJohn Marino 
167*d30dc8cbSJohn Marino          mpfr_div (v, y, x, GMP_RNDD); /* error 1 ulp */
168*d30dc8cbSJohn Marino          mpfr_sqr (v, v, GMP_RNDD);
169*d30dc8cbSJohn Marino             /* generic error of multiplication:
170*d30dc8cbSJohn Marino                1 + 2*1*(2+1*2^(1-prec)) <= 5.0625 since prec >= 6 */
171*d30dc8cbSJohn Marino          mpfr_log1p (v, v, GMP_RNDD);
172*d30dc8cbSJohn Marino             /* error 1 + 4*5.0625 = 21.25 , see algorithms.tex */
173*d30dc8cbSJohn Marino          mpfr_div_2ui (v, v, 1, GMP_RNDD);
174*d30dc8cbSJohn Marino             /* If the result is 0, then there has been an underflow somewhere. */
175*d30dc8cbSJohn Marino 
176*d30dc8cbSJohn Marino          mpfr_abs (w, x, GMP_RNDN); /* exact */
177*d30dc8cbSJohn Marino          mpfr_log (w, w, GMP_RNDN); /* error 0.5 ulp */
178*d30dc8cbSJohn Marino          expw = mpfr_get_exp (w);
179*d30dc8cbSJohn Marino          sgnw = mpfr_signbit (w);
180*d30dc8cbSJohn Marino 
181*d30dc8cbSJohn Marino          mpfr_add (w, w, v, GMP_RNDN);
182*d30dc8cbSJohn Marino          if (!sgnw) /* v is positive, so no cancellation;
183*d30dc8cbSJohn Marino                        error 22.25 ulp; error counts lost bits */
184*d30dc8cbSJohn Marino             err = 5;
185*d30dc8cbSJohn Marino          else
186*d30dc8cbSJohn Marino             err =   MPC_MAX (5 + mpfr_get_exp (v),
187*d30dc8cbSJohn Marino                   /* 21.25 ulp (v) rewritten in ulp (result, now in w) */
188*d30dc8cbSJohn Marino                            -1 + expw             - mpfr_get_exp (w)
189*d30dc8cbSJohn Marino                   /* 0.5 ulp (previous w), rewritten in ulp (result) */
190*d30dc8cbSJohn Marino                   ) + 2;
191*d30dc8cbSJohn Marino 
192*d30dc8cbSJohn Marino          /* handle one special case: |x|=1, and (y/x)^2 underflows;
193*d30dc8cbSJohn Marino             then 1/2*log(x^2+y^2) \approx 1/2*y^2 also underflows.  */
194*d30dc8cbSJohn Marino          if (   (mpfr_cmp_si (x, -1) == 0 || mpfr_cmp_ui (x, 1) == 0)
195*d30dc8cbSJohn Marino              && mpfr_zero_p (w))
196*d30dc8cbSJohn Marino             underflow = 1;
197*d30dc8cbSJohn Marino 
198*d30dc8cbSJohn Marino       } while (!underflow &&
199*d30dc8cbSJohn Marino                !mpfr_can_round (w, prec - err, GMP_RNDN, GMP_RNDZ,
200*d30dc8cbSJohn Marino                mpfr_get_prec (mpc_realref (rop)) + (MPC_RND_RE (rnd) == GMP_RNDN)));
201*d30dc8cbSJohn Marino       mpfr_clear (v);
202*d30dc8cbSJohn Marino    }
203*d30dc8cbSJohn Marino 
204*d30dc8cbSJohn Marino    /* imaginary part */
205*d30dc8cbSJohn Marino    inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op),
206*d30dc8cbSJohn Marino                          MPC_RND_IM (rnd));
207*d30dc8cbSJohn Marino 
208*d30dc8cbSJohn Marino    /* set the real part; cannot be done before if rop==op */
209*d30dc8cbSJohn Marino    if (underflow)
210*d30dc8cbSJohn Marino       /* create underflow in result */
211*d30dc8cbSJohn Marino       inex_re = mpfr_set_ui_2exp (mpc_realref (rop), 1,
212*d30dc8cbSJohn Marino                                   mpfr_get_emin_min () - 2, MPC_RND_RE (rnd));
213*d30dc8cbSJohn Marino    else
214*d30dc8cbSJohn Marino       inex_re = mpfr_set (mpc_realref (rop), w, MPC_RND_RE (rnd));
215*d30dc8cbSJohn Marino    mpfr_clear (w);
216*d30dc8cbSJohn Marino    return MPC_INEX(inex_re, inex_im);
217*d30dc8cbSJohn Marino }
218