xref: /netbsd-src/external/lgpl3/mpc/dist/src/log.c (revision 39f28e1e142c5bfb6be935a49cb55e2287fec7ea)
18fa80f29Smrg /* mpc_log -- Take the logarithm of a complex number.
28fa80f29Smrg 
38fa80f29Smrg Copyright (C) 2008, 2009, 2010, 2011, 2012 INRIA
48fa80f29Smrg 
58fa80f29Smrg This file is part of GNU MPC.
68fa80f29Smrg 
78fa80f29Smrg GNU MPC is free software; you can redistribute it and/or modify it under
88fa80f29Smrg the terms of the GNU Lesser General Public License as published by the
98fa80f29Smrg Free Software Foundation; either version 3 of the License, or (at your
108fa80f29Smrg option) any later version.
118fa80f29Smrg 
128fa80f29Smrg GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
138fa80f29Smrg WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
148fa80f29Smrg FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
158fa80f29Smrg more details.
168fa80f29Smrg 
178fa80f29Smrg You should have received a copy of the GNU Lesser General Public License
188fa80f29Smrg along with this program. If not, see http://www.gnu.org/licenses/ .
198fa80f29Smrg */
208fa80f29Smrg 
218fa80f29Smrg #include <stdio.h> /* for MPC_ASSERT */
228fa80f29Smrg #include "mpc-impl.h"
238fa80f29Smrg 
248fa80f29Smrg int
mpc_log(mpc_ptr rop,mpc_srcptr op,mpc_rnd_t rnd)258fa80f29Smrg mpc_log (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd){
268fa80f29Smrg    int ok, underflow = 0;
278fa80f29Smrg    mpfr_srcptr x, y;
288fa80f29Smrg    mpfr_t v, w;
298fa80f29Smrg    mpfr_prec_t prec;
308fa80f29Smrg    int loops;
318fa80f29Smrg    int re_cmp, im_cmp;
328fa80f29Smrg    int inex_re, inex_im;
338fa80f29Smrg    int err;
348fa80f29Smrg    mpfr_exp_t expw;
358fa80f29Smrg    int sgnw;
368fa80f29Smrg 
378fa80f29Smrg    /* special values: NaN and infinities */
388fa80f29Smrg    if (!mpc_fin_p (op)) {
398fa80f29Smrg       if (mpfr_nan_p (mpc_realref (op))) {
408fa80f29Smrg          if (mpfr_inf_p (mpc_imagref (op)))
418fa80f29Smrg             mpfr_set_inf (mpc_realref (rop), +1);
428fa80f29Smrg          else
438fa80f29Smrg             mpfr_set_nan (mpc_realref (rop));
448fa80f29Smrg          mpfr_set_nan (mpc_imagref (rop));
458fa80f29Smrg          inex_im = 0; /* Inf/NaN is exact */
468fa80f29Smrg       }
478fa80f29Smrg       else if (mpfr_nan_p (mpc_imagref (op))) {
488fa80f29Smrg          if (mpfr_inf_p (mpc_realref (op)))
498fa80f29Smrg             mpfr_set_inf (mpc_realref (rop), +1);
508fa80f29Smrg          else
518fa80f29Smrg             mpfr_set_nan (mpc_realref (rop));
528fa80f29Smrg          mpfr_set_nan (mpc_imagref (rop));
538fa80f29Smrg          inex_im = 0; /* Inf/NaN is exact */
548fa80f29Smrg       }
558fa80f29Smrg       else /* We have an infinity in at least one part. */ {
568fa80f29Smrg          inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op),
578fa80f29Smrg                                MPC_RND_IM (rnd));
588fa80f29Smrg          mpfr_set_inf (mpc_realref (rop), +1);
598fa80f29Smrg       }
608fa80f29Smrg       return MPC_INEX(0, inex_im);
618fa80f29Smrg    }
628fa80f29Smrg 
638fa80f29Smrg    /* special cases: real and purely imaginary numbers */
648fa80f29Smrg    re_cmp = mpfr_cmp_ui (mpc_realref (op), 0);
658fa80f29Smrg    im_cmp = mpfr_cmp_ui (mpc_imagref (op), 0);
668fa80f29Smrg    if (im_cmp == 0) {
678fa80f29Smrg       if (re_cmp == 0) {
688fa80f29Smrg          inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op),
698fa80f29Smrg                                MPC_RND_IM (rnd));
708fa80f29Smrg          mpfr_set_inf (mpc_realref (rop), -1);
718fa80f29Smrg          inex_re = 0; /* -Inf is exact */
728fa80f29Smrg       }
738fa80f29Smrg       else if (re_cmp > 0) {
748fa80f29Smrg          inex_re = mpfr_log (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd));
758fa80f29Smrg          inex_im = mpfr_set (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd));
768fa80f29Smrg       }
778fa80f29Smrg       else {
788fa80f29Smrg          /* op = x + 0*y; let w = -x = |x| */
798fa80f29Smrg          int negative_zero;
808fa80f29Smrg          mpfr_rnd_t rnd_im;
818fa80f29Smrg 
828fa80f29Smrg          negative_zero = mpfr_signbit (mpc_imagref (op));
838fa80f29Smrg          if (negative_zero)
848fa80f29Smrg             rnd_im = INV_RND (MPC_RND_IM (rnd));
858fa80f29Smrg          else
868fa80f29Smrg             rnd_im = MPC_RND_IM (rnd);
878fa80f29Smrg          w [0] = *mpc_realref (op);
888fa80f29Smrg          MPFR_CHANGE_SIGN (w);
898fa80f29Smrg          inex_re = mpfr_log (mpc_realref (rop), w, MPC_RND_RE (rnd));
908fa80f29Smrg          inex_im = mpfr_const_pi (mpc_imagref (rop), rnd_im);
918fa80f29Smrg          if (negative_zero) {
928fa80f29Smrg             mpc_conj (rop, rop, MPC_RNDNN);
938fa80f29Smrg             inex_im = -inex_im;
948fa80f29Smrg          }
958fa80f29Smrg       }
968fa80f29Smrg       return MPC_INEX(inex_re, inex_im);
978fa80f29Smrg    }
988fa80f29Smrg    else if (re_cmp == 0) {
998fa80f29Smrg       if (im_cmp > 0) {
1008fa80f29Smrg          inex_re = mpfr_log (mpc_realref (rop), mpc_imagref (op), MPC_RND_RE (rnd));
1018fa80f29Smrg          inex_im = mpfr_const_pi (mpc_imagref (rop), MPC_RND_IM (rnd));
1028fa80f29Smrg          /* division by 2 does not change the ternary flag */
103*39f28e1eSmrg          mpfr_div_2ui (mpc_imagref (rop), mpc_imagref (rop), 1, MPFR_RNDN);
1048fa80f29Smrg       }
1058fa80f29Smrg       else {
1068fa80f29Smrg          w [0] = *mpc_imagref (op);
1078fa80f29Smrg          MPFR_CHANGE_SIGN (w);
1088fa80f29Smrg          inex_re = mpfr_log (mpc_realref (rop), w, MPC_RND_RE (rnd));
1098fa80f29Smrg          inex_im = mpfr_const_pi (mpc_imagref (rop), INV_RND (MPC_RND_IM (rnd)));
1108fa80f29Smrg          /* division by 2 does not change the ternary flag */
111*39f28e1eSmrg          mpfr_div_2ui (mpc_imagref (rop), mpc_imagref (rop), 1, MPFR_RNDN);
112*39f28e1eSmrg          mpfr_neg (mpc_imagref (rop), mpc_imagref (rop), MPFR_RNDN);
1138fa80f29Smrg          inex_im = -inex_im; /* negate the ternary flag */
1148fa80f29Smrg       }
1158fa80f29Smrg       return MPC_INEX(inex_re, inex_im);
1168fa80f29Smrg    }
1178fa80f29Smrg 
1188fa80f29Smrg    prec = MPC_PREC_RE(rop);
1198fa80f29Smrg    mpfr_init2 (w, 2);
1208fa80f29Smrg    /* let op = x + iy; log = 1/2 log (x^2 + y^2) + i atan2 (y, x)   */
1218fa80f29Smrg    /* loop for the real part: 1/2 log (x^2 + y^2), fast, but unsafe */
1228fa80f29Smrg    /* implementation                                                */
1238fa80f29Smrg    ok = 0;
1248fa80f29Smrg    for (loops = 1; !ok && loops <= 2; loops++) {
1258fa80f29Smrg       prec += mpc_ceil_log2 (prec) + 4;
1268fa80f29Smrg       mpfr_set_prec (w, prec);
1278fa80f29Smrg 
128*39f28e1eSmrg       mpc_abs (w, op, MPFR_RNDN);
1298fa80f29Smrg          /* error 0.5 ulp */
1308fa80f29Smrg       if (mpfr_inf_p (w))
1318fa80f29Smrg          /* intermediate overflow; the logarithm may be representable.
1328fa80f29Smrg             Intermediate underflow is impossible.                      */
1338fa80f29Smrg          break;
1348fa80f29Smrg 
135*39f28e1eSmrg       mpfr_log (w, w, MPFR_RNDN);
1368fa80f29Smrg          /* generic error of log: (2^(- exp(w)) + 0.5) ulp */
1378fa80f29Smrg 
1388fa80f29Smrg       if (mpfr_zero_p (w))
1398fa80f29Smrg          /* impossible to round, switch to second algorithm */
1408fa80f29Smrg          break;
1418fa80f29Smrg 
1428fa80f29Smrg       err = MPC_MAX (-mpfr_get_exp (w), 0) + 1;
1438fa80f29Smrg          /* number of lost digits */
144*39f28e1eSmrg       ok = mpfr_can_round (w, prec - err, MPFR_RNDN, MPFR_RNDZ,
145*39f28e1eSmrg          mpfr_get_prec (mpc_realref (rop)) + (MPC_RND_RE (rnd) == MPFR_RNDN));
1468fa80f29Smrg    }
1478fa80f29Smrg 
1488fa80f29Smrg    if (!ok) {
1498fa80f29Smrg       prec = MPC_PREC_RE(rop);
1508fa80f29Smrg       mpfr_init2 (v, 2);
1518fa80f29Smrg       /* compute 1/2 log (x^2 + y^2) = log |x| + 1/2 * log (1 + (y/x)^2)
1528fa80f29Smrg             if |x| >= |y|; otherwise, exchange x and y                   */
1538fa80f29Smrg       if (mpfr_cmpabs (mpc_realref (op), mpc_imagref (op)) >= 0) {
1548fa80f29Smrg          x = mpc_realref (op);
1558fa80f29Smrg          y = mpc_imagref (op);
1568fa80f29Smrg       }
1578fa80f29Smrg       else {
1588fa80f29Smrg          x = mpc_imagref (op);
1598fa80f29Smrg          y = mpc_realref (op);
1608fa80f29Smrg       }
1618fa80f29Smrg 
1628fa80f29Smrg       do {
1638fa80f29Smrg          prec += mpc_ceil_log2 (prec) + 4;
1648fa80f29Smrg          mpfr_set_prec (v, prec);
1658fa80f29Smrg          mpfr_set_prec (w, prec);
1668fa80f29Smrg 
167*39f28e1eSmrg          mpfr_div (v, y, x, MPFR_RNDD); /* error 1 ulp */
168*39f28e1eSmrg          mpfr_sqr (v, v, MPFR_RNDD);
1698fa80f29Smrg             /* generic error of multiplication:
1708fa80f29Smrg                1 + 2*1*(2+1*2^(1-prec)) <= 5.0625 since prec >= 6 */
171*39f28e1eSmrg          mpfr_log1p (v, v, MPFR_RNDD);
1728fa80f29Smrg             /* error 1 + 4*5.0625 = 21.25 , see algorithms.tex */
173*39f28e1eSmrg          mpfr_div_2ui (v, v, 1, MPFR_RNDD);
1748fa80f29Smrg             /* If the result is 0, then there has been an underflow somewhere. */
1758fa80f29Smrg 
176*39f28e1eSmrg          mpfr_abs (w, x, MPFR_RNDN); /* exact */
177*39f28e1eSmrg          mpfr_log (w, w, MPFR_RNDN); /* error 0.5 ulp */
1788fa80f29Smrg          expw = mpfr_get_exp (w);
1798fa80f29Smrg          sgnw = mpfr_signbit (w);
1808fa80f29Smrg 
181*39f28e1eSmrg          mpfr_add (w, w, v, MPFR_RNDN);
1828fa80f29Smrg          if (!sgnw) /* v is positive, so no cancellation;
1838fa80f29Smrg                        error 22.25 ulp; error counts lost bits */
1848fa80f29Smrg             err = 5;
1858fa80f29Smrg          else
1868fa80f29Smrg             err =   MPC_MAX (5 + mpfr_get_exp (v),
1878fa80f29Smrg                   /* 21.25 ulp (v) rewritten in ulp (result, now in w) */
1888fa80f29Smrg                            -1 + expw             - mpfr_get_exp (w)
1898fa80f29Smrg                   /* 0.5 ulp (previous w), rewritten in ulp (result) */
1908fa80f29Smrg                   ) + 2;
1918fa80f29Smrg 
1928fa80f29Smrg          /* handle one special case: |x|=1, and (y/x)^2 underflows;
1938fa80f29Smrg             then 1/2*log(x^2+y^2) \approx 1/2*y^2 also underflows.  */
1948fa80f29Smrg          if (   (mpfr_cmp_si (x, -1) == 0 || mpfr_cmp_ui (x, 1) == 0)
1958fa80f29Smrg              && mpfr_zero_p (w))
1968fa80f29Smrg             underflow = 1;
1978fa80f29Smrg 
1988fa80f29Smrg       } while (!underflow &&
199*39f28e1eSmrg                !mpfr_can_round (w, prec - err, MPFR_RNDN, MPFR_RNDZ,
200*39f28e1eSmrg                mpfr_get_prec (mpc_realref (rop)) + (MPC_RND_RE (rnd) == MPFR_RNDN)));
2018fa80f29Smrg       mpfr_clear (v);
2028fa80f29Smrg    }
2038fa80f29Smrg 
2048fa80f29Smrg    /* imaginary part */
2058fa80f29Smrg    inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op),
2068fa80f29Smrg                          MPC_RND_IM (rnd));
2078fa80f29Smrg 
2088fa80f29Smrg    /* set the real part; cannot be done before if rop==op */
2098fa80f29Smrg    if (underflow)
2108fa80f29Smrg       /* create underflow in result */
2118fa80f29Smrg       inex_re = mpfr_set_ui_2exp (mpc_realref (rop), 1,
2128fa80f29Smrg                                   mpfr_get_emin_min () - 2, MPC_RND_RE (rnd));
2138fa80f29Smrg    else
2148fa80f29Smrg       inex_re = mpfr_set (mpc_realref (rop), w, MPC_RND_RE (rnd));
2158fa80f29Smrg    mpfr_clear (w);
2168fa80f29Smrg    return MPC_INEX(inex_re, inex_im);
2178fa80f29Smrg }
218