Blame src/log10.c

Packit Service 2e9770
/* mpc_log10 -- Take the base-10 logarithm of a complex number.
Packit Service 2e9770
Packit Service 2e9770
Copyright (C) 2012 INRIA
Packit Service 2e9770
Packit Service 2e9770
This file is part of GNU MPC.
Packit Service 2e9770
Packit Service 2e9770
GNU MPC is free software; you can redistribute it and/or modify it under
Packit Service 2e9770
the terms of the GNU Lesser General Public License as published by the
Packit Service 2e9770
Free Software Foundation; either version 3 of the License, or (at your
Packit Service 2e9770
option) any later version.
Packit Service 2e9770
Packit Service 2e9770
GNU MPC is distributed in the hope that it will be useful, but WITHOUT ANY
Packit Service 2e9770
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
Packit Service 2e9770
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
Packit Service 2e9770
more details.
Packit Service 2e9770
Packit Service 2e9770
You should have received a copy of the GNU Lesser General Public License
Packit Service 2e9770
along with this program. If not, see http://logw.gnu.org/licenses/ .
Packit Service 2e9770
*/
Packit Service 2e9770
Packit Service 2e9770
#include <limits.h> /* for CHAR_BIT */
Packit Service 2e9770
#include "mpc-impl.h"
Packit Service 2e9770
Packit Service 2e9770
static void
Packit Service 2e9770
mpfr_const_log10 (mpfr_ptr log10)
Packit Service 2e9770
{
Packit Service 2e9770
   mpfr_set_ui (log10, 10, MPFR_RNDN); /* exact since prec >= 4 */
Packit Service 2e9770
   mpfr_log (log10, log10, MPFR_RNDN); /* error <= 1/2 ulp */
Packit Service 2e9770
}
Packit Service 2e9770
Packit Service 2e9770
Packit Service 2e9770
int
Packit Service 2e9770
mpc_log10 (mpc_ptr rop, mpc_srcptr op, mpc_rnd_t rnd)
Packit Service 2e9770
{
Packit Service 2e9770
   int ok = 0, loops = 0, check_exact = 0, special_re, special_im,
Packit Service 2e9770
       inex, inex_re, inex_im;
Packit Service 2e9770
   mpfr_prec_t prec;
Packit Service 2e9770
   mpfr_t log10;
Packit Service 2e9770
   mpc_t log;
Packit Service 2e9770
Packit Service 2e9770
   mpfr_init2 (log10, 2);
Packit Service 2e9770
   mpc_init2 (log, 2);
Packit Service 2e9770
   prec = MPC_MAX_PREC (rop);
Packit Service 2e9770
   /* compute log(op)/log(10) */
Packit Service 2e9770
   while (ok == 0) {
Packit Service 2e9770
      loops ++;
Packit Service 2e9770
      prec += (loops <= 2) ? mpc_ceil_log2 (prec) + 4 : prec / 2;
Packit Service 2e9770
      mpfr_set_prec (log10, prec);
Packit Service 2e9770
      mpc_set_prec (log, prec);
Packit Service 2e9770
Packit Service 2e9770
      inex = mpc_log (log, op, rnd); /* error <= 1 ulp */
Packit Service 2e9770
Packit Service 2e9770
      if (!mpfr_number_p (mpc_imagref (log))
Packit Service 2e9770
         || mpfr_zero_p (mpc_imagref (log))) {
Packit Service 2e9770
         /* no need to divide by log(10) */
Packit Service 2e9770
         special_im = 1;
Packit Service 2e9770
         ok = 1;
Packit Service 2e9770
      }
Packit Service 2e9770
      else {
Packit Service 2e9770
         special_im = 0;
Packit Service 2e9770
         mpfr_const_log10 (log10);
Packit Service 2e9770
         mpfr_div (mpc_imagref (log), mpc_imagref (log), log10, MPFR_RNDN);
Packit Service 2e9770
Packit Service 2e9770
         ok = mpfr_can_round (mpc_imagref (log), prec - 2,
Packit Service 2e9770
                  MPFR_RNDN, MPFR_RNDZ,
Packit Service 2e9770
                  MPC_PREC_IM(rop) + (MPC_RND_IM (rnd) == MPFR_RNDN));
Packit Service 2e9770
      }
Packit Service 2e9770
Packit Service 2e9770
      if (ok) {
Packit Service 2e9770
         if (!mpfr_number_p (mpc_realref (log))
Packit Service 2e9770
            || mpfr_zero_p (mpc_realref (log)))
Packit Service 2e9770
            special_re = 1;
Packit Service 2e9770
         else {
Packit Service 2e9770
            special_re = 0;
Packit Service 2e9770
            if (special_im)
Packit Service 2e9770
               /* log10 not yet computed */
Packit Service 2e9770
               mpfr_const_log10 (log10);
Packit Service 2e9770
            mpfr_div (mpc_realref (log), mpc_realref (log), log10, MPFR_RNDN);
Packit Service 2e9770
               /* error <= 24/7 ulp < 4 ulp for prec >= 4, see algorithms.tex */
Packit Service 2e9770
Packit Service 2e9770
            ok = mpfr_can_round (mpc_realref (log), prec - 2,
Packit Service 2e9770
                     MPFR_RNDN, MPFR_RNDZ,
Packit Service 2e9770
                     MPC_PREC_RE(rop) + (MPC_RND_RE (rnd) == MPFR_RNDN));
Packit Service 2e9770
         }
Packit Service 2e9770
Packit Service 2e9770
         /* Special code to deal with cases where the real part of log10(x+i*y)
Packit Service 2e9770
            is exact, like x=3 and y=1. Since Re(log10(x+i*y)) = log10(x^2+y^2)/2
Packit Service 2e9770
            this happens whenever x^2+y^2 is a nonnegative power of 10.
Packit Service 2e9770
            Indeed x^2+y^2 cannot equal 10^(a/2^b) for a, b integers, a odd, b>0,
Packit Service 2e9770
            since x^2+y^2 is rational, and 10^(a/2^b) is irrational.
Packit Service 2e9770
            Similarly, for b=0, x^2+y^2 cannot equal 10^a for a < 0 since x^2+y^2
Packit Service 2e9770
            is a rational with denominator a power of 2.
Packit Service 2e9770
            Now let x^2+y^2 = 10^s. Without loss of generality we can assume
Packit Service 2e9770
            x = u/2^e and y = v/2^e with u, v, e integers: u^2+v^2 = 10^s*2^(2e)
Packit Service 2e9770
            thus u^2+v^2 = 0 mod 2^(2e). By recurrence on e, necessarily
Packit Service 2e9770
            u = v = 0 mod 2^e, thus x and y are necessarily integers.
Packit Service 2e9770
         */
Packit Service 2e9770
         if (!ok && !check_exact && mpfr_integer_p (mpc_realref (op)) &&
Packit Service 2e9770
            mpfr_integer_p (mpc_imagref (op))) {
Packit Service 2e9770
            mpz_t x, y;
Packit Service 2e9770
            unsigned long s, v;
Packit Service 2e9770
Packit Service 2e9770
            check_exact = 1;
Packit Service 2e9770
            mpz_init (x);
Packit Service 2e9770
            mpz_init (y);
Packit Service 2e9770
            mpfr_get_z (x, mpc_realref (op), MPFR_RNDN); /* exact */
Packit Service 2e9770
            mpfr_get_z (y, mpc_imagref (op), MPFR_RNDN); /* exact */
Packit Service 2e9770
            mpz_mul (x, x, x);
Packit Service 2e9770
            mpz_mul (y, y, y);
Packit Service 2e9770
            mpz_add (x, x, y); /* x^2+y^2 */
Packit Service 2e9770
            v = mpz_scan1 (x, 0);
Packit Service 2e9770
            /* if x = 10^s then necessarily s = v */
Packit Service 2e9770
            s = mpz_sizeinbase (x, 10);
Packit Service 2e9770
            /* since s is either the number of digits of x or one more,
Packit Service 2e9770
               then x = 10^(s-1) or 10^(s-2) */
Packit Service 2e9770
            if (s == v + 1 || s == v + 2) {
Packit Service 2e9770
               mpz_div_2exp (x, x, v);
Packit Service 2e9770
               mpz_ui_pow_ui (y, 5, v);
Packit Service 2e9770
               if (mpz_cmp (y, x) == 0) {
Packit Service 2e9770
                  /* Re(log10(x+i*y)) is exactly v/2
Packit Service 2e9770
                     we reset the precision of Re(log) so that v can be
Packit Service 2e9770
                     represented exactly */
Packit Service 2e9770
                  mpfr_set_prec (mpc_realref (log),
Packit Service 2e9770
                                 sizeof(unsigned long)*CHAR_BIT);
Packit Service 2e9770
                  mpfr_set_ui_2exp (mpc_realref (log), v, -1, MPFR_RNDN);
Packit Service 2e9770
                     /* exact */
Packit Service 2e9770
                  ok = 1;
Packit Service 2e9770
               }
Packit Service 2e9770
            }
Packit Service 2e9770
            mpz_clear (x);
Packit Service 2e9770
            mpz_clear (y);
Packit Service 2e9770
         }
Packit Service 2e9770
      }
Packit Service 2e9770
   }
Packit Service 2e9770
Packit Service 2e9770
   inex_re = mpfr_set (mpc_realref(rop), mpc_realref (log), MPC_RND_RE (rnd));
Packit Service 2e9770
   if (special_re)
Packit Service 2e9770
      inex_re = MPC_INEX_RE (inex);
Packit Service 2e9770
      /* recover flag from call to mpc_log above */
Packit Service 2e9770
   inex_im = mpfr_set (mpc_imagref(rop), mpc_imagref (log), MPC_RND_IM (rnd));
Packit Service 2e9770
   if (special_im)
Packit Service 2e9770
      inex_im = MPC_INEX_IM (inex);
Packit Service 2e9770
   mpfr_clear (log10);
Packit Service 2e9770
   mpc_clear (log);
Packit Service 2e9770
Packit Service 2e9770
   return MPC_INEX(inex_re, inex_im);
Packit Service 2e9770
}