Blame src/log.c

Packit 80c72f
/* mpc_log -- Take the logarithm of a complex number.
Packit 80c72f
Packit 80c72f
Copyright (C) 2008, 2009, 2010, 2011, 2012 INRIA
Packit 80c72f
Packit 80c72f
This file is part of GNU MPC.
Packit 80c72f
Packit 80c72f
GNU MPC is free software; you can redistribute it and/or modify it under
Packit 80c72f
the terms of the GNU Lesser General Public License as published by the
Packit 80c72f
Free Software Foundation; either version 3 of the License, or (at your
Packit 80c72f
option) any later version.
Packit 80c72f
Packit 80c72f
GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
Packit 80c72f
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
Packit 80c72f
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
Packit 80c72f
more details.
Packit 80c72f
Packit 80c72f
You should have received a copy of the GNU Lesser General Public License
Packit 80c72f
along with this program. If not, see http://www.gnu.org/licenses/ .
Packit 80c72f
*/
Packit 80c72f
Packit 80c72f
#include <stdio.h> /* for MPC_ASSERT */
Packit 80c72f
#include "mpc-impl.h"
Packit 80c72f
Packit 80c72f
int
Packit 80c72f
mpc_log (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd){
Packit 80c72f
   int ok, underflow = 0;
Packit 80c72f
   mpfr_srcptr x, y;
Packit 80c72f
   mpfr_t v, w;
Packit 80c72f
   mpfr_prec_t prec;
Packit 80c72f
   int loops;
Packit 80c72f
   int re_cmp, im_cmp;
Packit 80c72f
   int inex_re, inex_im;
Packit 80c72f
   int err;
Packit 80c72f
   mpfr_exp_t expw;
Packit 80c72f
   int sgnw;
Packit 80c72f
Packit 80c72f
   /* special values: NaN and infinities */
Packit 80c72f
   if (!mpc_fin_p (op)) {
Packit 80c72f
      if (mpfr_nan_p (mpc_realref (op))) {
Packit 80c72f
         if (mpfr_inf_p (mpc_imagref (op)))
Packit 80c72f
            mpfr_set_inf (mpc_realref (rop), +1);
Packit 80c72f
         else
Packit 80c72f
            mpfr_set_nan (mpc_realref (rop));
Packit 80c72f
         mpfr_set_nan (mpc_imagref (rop));
Packit 80c72f
         inex_im = 0; /* Inf/NaN is exact */
Packit 80c72f
      }
Packit 80c72f
      else if (mpfr_nan_p (mpc_imagref (op))) {
Packit 80c72f
         if (mpfr_inf_p (mpc_realref (op)))
Packit 80c72f
            mpfr_set_inf (mpc_realref (rop), +1);
Packit 80c72f
         else
Packit 80c72f
            mpfr_set_nan (mpc_realref (rop));
Packit 80c72f
         mpfr_set_nan (mpc_imagref (rop));
Packit 80c72f
         inex_im = 0; /* Inf/NaN is exact */
Packit 80c72f
      }
Packit 80c72f
      else /* We have an infinity in at least one part. */ {
Packit 80c72f
         inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op),
Packit 80c72f
                               MPC_RND_IM (rnd));
Packit 80c72f
         mpfr_set_inf (mpc_realref (rop), +1);
Packit 80c72f
      }
Packit 80c72f
      return MPC_INEX(0, inex_im);
Packit 80c72f
   }
Packit 80c72f
Packit 80c72f
   /* special cases: real and purely imaginary numbers */
Packit 80c72f
   re_cmp = mpfr_cmp_ui (mpc_realref (op), 0);
Packit 80c72f
   im_cmp = mpfr_cmp_ui (mpc_imagref (op), 0);
Packit 80c72f
   if (im_cmp == 0) {
Packit 80c72f
      if (re_cmp == 0) {
Packit 80c72f
         inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op),
Packit 80c72f
                               MPC_RND_IM (rnd));
Packit 80c72f
         mpfr_set_inf (mpc_realref (rop), -1);
Packit 80c72f
         inex_re = 0; /* -Inf is exact */
Packit 80c72f
      }
Packit 80c72f
      else if (re_cmp > 0) {
Packit 80c72f
         inex_re = mpfr_log (mpc_realref (rop), mpc_realref (op), MPC_RND_RE (rnd));
Packit 80c72f
         inex_im = mpfr_set (mpc_imagref (rop), mpc_imagref (op), MPC_RND_IM (rnd));
Packit 80c72f
      }
Packit 80c72f
      else {
Packit 80c72f
         /* op = x + 0*y; let w = -x = |x| */
Packit 80c72f
         int negative_zero;
Packit 80c72f
         mpfr_rnd_t rnd_im;
Packit 80c72f
Packit 80c72f
         negative_zero = mpfr_signbit (mpc_imagref (op));
Packit 80c72f
         if (negative_zero)
Packit 80c72f
            rnd_im = INV_RND (MPC_RND_IM (rnd));
Packit 80c72f
         else
Packit 80c72f
            rnd_im = MPC_RND_IM (rnd);
Packit 80c72f
         w [0] = *mpc_realref (op);
Packit 80c72f
         MPFR_CHANGE_SIGN (w);
Packit 80c72f
         inex_re = mpfr_log (mpc_realref (rop), w, MPC_RND_RE (rnd));
Packit 80c72f
         inex_im = mpfr_const_pi (mpc_imagref (rop), rnd_im);
Packit 80c72f
         if (negative_zero) {
Packit 80c72f
            mpc_conj (rop, rop, MPC_RNDNN);
Packit 80c72f
            inex_im = -inex_im;
Packit 80c72f
         }
Packit 80c72f
      }
Packit 80c72f
      return MPC_INEX(inex_re, inex_im);
Packit 80c72f
   }
Packit 80c72f
   else if (re_cmp == 0) {
Packit 80c72f
      if (im_cmp > 0) {
Packit 80c72f
         inex_re = mpfr_log (mpc_realref (rop), mpc_imagref (op), MPC_RND_RE (rnd));
Packit 80c72f
         inex_im = mpfr_const_pi (mpc_imagref (rop), MPC_RND_IM (rnd));
Packit 80c72f
         /* division by 2 does not change the ternary flag */
Packit 80c72f
         mpfr_div_2ui (mpc_imagref (rop), mpc_imagref (rop), 1, GMP_RNDN);
Packit 80c72f
      }
Packit 80c72f
      else {
Packit 80c72f
         w [0] = *mpc_imagref (op);
Packit 80c72f
         MPFR_CHANGE_SIGN (w);
Packit 80c72f
         inex_re = mpfr_log (mpc_realref (rop), w, MPC_RND_RE (rnd));
Packit 80c72f
         inex_im = mpfr_const_pi (mpc_imagref (rop), INV_RND (MPC_RND_IM (rnd)));
Packit 80c72f
         /* division by 2 does not change the ternary flag */
Packit 80c72f
         mpfr_div_2ui (mpc_imagref (rop), mpc_imagref (rop), 1, GMP_RNDN);
Packit 80c72f
         mpfr_neg (mpc_imagref (rop), mpc_imagref (rop), GMP_RNDN);
Packit 80c72f
         inex_im = -inex_im; /* negate the ternary flag */
Packit 80c72f
      }
Packit 80c72f
      return MPC_INEX(inex_re, inex_im);
Packit 80c72f
   }
Packit 80c72f
Packit 80c72f
   prec = MPC_PREC_RE(rop);
Packit 80c72f
   mpfr_init2 (w, 2);
Packit 80c72f
   /* let op = x + iy; log = 1/2 log (x^2 + y^2) + i atan2 (y, x)   */
Packit 80c72f
   /* loop for the real part: 1/2 log (x^2 + y^2), fast, but unsafe */
Packit 80c72f
   /* implementation                                                */
Packit 80c72f
   ok = 0;
Packit 80c72f
   for (loops = 1; !ok && loops <= 2; loops++) {
Packit 80c72f
      prec += mpc_ceil_log2 (prec) + 4;
Packit 80c72f
      mpfr_set_prec (w, prec);
Packit 80c72f
Packit 80c72f
      mpc_abs (w, op, GMP_RNDN);
Packit 80c72f
         /* error 0.5 ulp */
Packit 80c72f
      if (mpfr_inf_p (w))
Packit 80c72f
         /* intermediate overflow; the logarithm may be representable.
Packit 80c72f
            Intermediate underflow is impossible.                      */
Packit 80c72f
         break;
Packit 80c72f
Packit 80c72f
      mpfr_log (w, w, GMP_RNDN);
Packit 80c72f
         /* generic error of log: (2^(- exp(w)) + 0.5) ulp */
Packit 80c72f
Packit 80c72f
      if (mpfr_zero_p (w))
Packit 80c72f
         /* impossible to round, switch to second algorithm */
Packit 80c72f
         break;
Packit 80c72f
Packit 80c72f
      err = MPC_MAX (-mpfr_get_exp (w), 0) + 1;
Packit 80c72f
         /* number of lost digits */
Packit 80c72f
      ok = mpfr_can_round (w, prec - err, GMP_RNDN, GMP_RNDZ,
Packit 80c72f
         mpfr_get_prec (mpc_realref (rop)) + (MPC_RND_RE (rnd) == GMP_RNDN));
Packit 80c72f
   }
Packit 80c72f
Packit 80c72f
   if (!ok) {
Packit 80c72f
      prec = MPC_PREC_RE(rop);
Packit 80c72f
      mpfr_init2 (v, 2);
Packit 80c72f
      /* compute 1/2 log (x^2 + y^2) = log |x| + 1/2 * log (1 + (y/x)^2)
Packit 80c72f
            if |x| >= |y|; otherwise, exchange x and y                   */
Packit 80c72f
      if (mpfr_cmpabs (mpc_realref (op), mpc_imagref (op)) >= 0) {
Packit 80c72f
         x = mpc_realref (op);
Packit 80c72f
         y = mpc_imagref (op);
Packit 80c72f
      }
Packit 80c72f
      else {
Packit 80c72f
         x = mpc_imagref (op);
Packit 80c72f
         y = mpc_realref (op);
Packit 80c72f
      }
Packit 80c72f
Packit 80c72f
      do {
Packit 80c72f
         prec += mpc_ceil_log2 (prec) + 4;
Packit 80c72f
         mpfr_set_prec (v, prec);
Packit 80c72f
         mpfr_set_prec (w, prec);
Packit 80c72f
Packit 80c72f
         mpfr_div (v, y, x, GMP_RNDD); /* error 1 ulp */
Packit 80c72f
         mpfr_sqr (v, v, GMP_RNDD);
Packit 80c72f
            /* generic error of multiplication:
Packit 80c72f
               1 + 2*1*(2+1*2^(1-prec)) <= 5.0625 since prec >= 6 */
Packit 80c72f
         mpfr_log1p (v, v, GMP_RNDD);
Packit 80c72f
            /* error 1 + 4*5.0625 = 21.25 , see algorithms.tex */
Packit 80c72f
         mpfr_div_2ui (v, v, 1, GMP_RNDD);
Packit 80c72f
            /* If the result is 0, then there has been an underflow somewhere. */
Packit 80c72f
Packit 80c72f
         mpfr_abs (w, x, GMP_RNDN); /* exact */
Packit 80c72f
         mpfr_log (w, w, GMP_RNDN); /* error 0.5 ulp */
Packit 80c72f
         expw = mpfr_get_exp (w);
Packit 80c72f
         sgnw = mpfr_signbit (w);
Packit 80c72f
Packit 80c72f
         mpfr_add (w, w, v, GMP_RNDN);
Packit 80c72f
         if (!sgnw) /* v is positive, so no cancellation;
Packit 80c72f
                       error 22.25 ulp; error counts lost bits */
Packit 80c72f
            err = 5;
Packit 80c72f
         else
Packit 80c72f
            err =   MPC_MAX (5 + mpfr_get_exp (v),
Packit 80c72f
                  /* 21.25 ulp (v) rewritten in ulp (result, now in w) */
Packit 80c72f
                           -1 + expw             - mpfr_get_exp (w)
Packit 80c72f
                  /* 0.5 ulp (previous w), rewritten in ulp (result) */
Packit 80c72f
                  ) + 2;
Packit 80c72f
Packit 80c72f
         /* handle one special case: |x|=1, and (y/x)^2 underflows;
Packit 80c72f
            then 1/2*log(x^2+y^2) \approx 1/2*y^2 also underflows.  */
Packit 80c72f
         if (   (mpfr_cmp_si (x, -1) == 0 || mpfr_cmp_ui (x, 1) == 0)
Packit 80c72f
             && mpfr_zero_p (w))
Packit 80c72f
            underflow = 1;
Packit 80c72f
Packit 80c72f
      } while (!underflow &&
Packit 80c72f
               !mpfr_can_round (w, prec - err, GMP_RNDN, GMP_RNDZ,
Packit 80c72f
               mpfr_get_prec (mpc_realref (rop)) + (MPC_RND_RE (rnd) == GMP_RNDN)));
Packit 80c72f
      mpfr_clear (v);
Packit 80c72f
   }
Packit 80c72f
Packit 80c72f
   /* imaginary part */
Packit 80c72f
   inex_im = mpfr_atan2 (mpc_imagref (rop), mpc_imagref (op), mpc_realref (op),
Packit 80c72f
                         MPC_RND_IM (rnd));
Packit 80c72f
Packit 80c72f
   /* set the real part; cannot be done before if rop==op */
Packit 80c72f
   if (underflow)
Packit 80c72f
      /* create underflow in result */
Packit 80c72f
      inex_re = mpfr_set_ui_2exp (mpc_realref (rop), 1,
Packit 80c72f
                                  mpfr_get_emin_min () - 2, MPC_RND_RE (rnd));
Packit 80c72f
   else
Packit 80c72f
      inex_re = mpfr_set (mpc_realref (rop), w, MPC_RND_RE (rnd));
Packit 80c72f
   mpfr_clear (w);
Packit 80c72f
   return MPC_INEX(inex_re, inex_im);
Packit 80c72f
}