Blame src/norm.c

Packit Service 2e9770
/* mpc_norm -- Square of the norm of a complex number.
Packit Service 2e9770
Packit Service 2e9770
Copyright (C) 2002, 2005, 2008, 2009, 2010, 2011, 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://www.gnu.org/licenses/ .
Packit Service 2e9770
*/
Packit Service 2e9770
Packit Service 2e9770
#include <stdio.h>    /* for MPC_ASSERT */
Packit Service 2e9770
#include "mpc-impl.h"
Packit Service 2e9770
Packit Service 2e9770
/* a <- norm(b) = b * conj(b)
Packit Service 2e9770
   (the rounding mode is mpfr_rnd_t here since we return an mpfr number) */
Packit Service 2e9770
int
Packit Service 2e9770
mpc_norm (mpfr_ptr a, mpc_srcptr b, mpfr_rnd_t rnd)
Packit Service 2e9770
{
Packit Service 2e9770
   int inexact;
Packit Service 2e9770
   int saved_underflow, saved_overflow;
Packit Service 2e9770
Packit Service 2e9770
   /* handling of special values; consistent with abs in that
Packit Service 2e9770
      norm = abs^2; so norm (+-inf, xxx) = norm (xxx, +-inf) = +inf */
Packit Service 2e9770
   if (!mpc_fin_p (b))
Packit Service 2e9770
         return mpc_abs (a, b, rnd);
Packit Service 2e9770
   else if (mpfr_zero_p (mpc_realref (b))) {
Packit Service 2e9770
      if (mpfr_zero_p (mpc_imagref (b)))
Packit Service 2e9770
         return mpfr_set_ui (a, 0, rnd); /* +0 */
Packit Service 2e9770
      else
Packit Service 2e9770
         return mpfr_sqr (a, mpc_imagref (b), rnd);
Packit Service 2e9770
   }
Packit Service 2e9770
   else if (mpfr_zero_p (mpc_imagref (b)))
Packit Service 2e9770
     return mpfr_sqr (a, mpc_realref (b), rnd); /* Re(b) <> 0 */
Packit Service 2e9770
Packit Service 2e9770
   else /* everything finite and non-zero */ {
Packit Service 2e9770
      mpfr_t u, v, res;
Packit Service 2e9770
      mpfr_prec_t prec, prec_u, prec_v;
Packit Service 2e9770
      int loops;
Packit Service 2e9770
      const int max_loops = 2;
Packit Service 2e9770
         /* switch to exact squarings when loops==max_loops */
Packit Service 2e9770
Packit Service 2e9770
      prec = mpfr_get_prec (a);
Packit Service 2e9770
Packit Service 2e9770
      mpfr_init (u);
Packit Service 2e9770
      mpfr_init (v);
Packit Service 2e9770
      mpfr_init (res);
Packit Service 2e9770
Packit Service 2e9770
      /* save the underflow or overflow flags from MPFR */
Packit Service 2e9770
      saved_underflow = mpfr_underflow_p ();
Packit Service 2e9770
      saved_overflow = mpfr_overflow_p ();
Packit Service 2e9770
Packit Service 2e9770
      loops = 0;
Packit Service 2e9770
      mpfr_clear_underflow ();
Packit Service 2e9770
      mpfr_clear_overflow ();
Packit Service 2e9770
      do {
Packit Service 2e9770
         loops++;
Packit Service 2e9770
         prec += mpc_ceil_log2 (prec) + 3;
Packit Service 2e9770
         if (loops >= max_loops) {
Packit Service 2e9770
            prec_u = 2 * MPC_PREC_RE (b);
Packit Service 2e9770
            prec_v = 2 * MPC_PREC_IM (b);
Packit Service 2e9770
         }
Packit Service 2e9770
         else {
Packit Service 2e9770
            prec_u = MPC_MIN (prec, 2 * MPC_PREC_RE (b));
Packit Service 2e9770
            prec_v = MPC_MIN (prec, 2 * MPC_PREC_IM (b));
Packit Service 2e9770
         }
Packit Service 2e9770
Packit Service 2e9770
         mpfr_set_prec (u, prec_u);
Packit Service 2e9770
         mpfr_set_prec (v, prec_v);
Packit Service 2e9770
Packit Service 2e9770
         inexact  = mpfr_sqr (u, mpc_realref(b), MPFR_RNDD); /* err <= 1 ulp in prec */
Packit Service 2e9770
         inexact |= mpfr_sqr (v, mpc_imagref(b), MPFR_RNDD); /* err <= 1 ulp in prec */
Packit Service 2e9770
Packit Service 2e9770
         /* If loops = max_loops, inexact should be 0 here, except in case
Packit Service 2e9770
               of underflow or overflow.
Packit Service 2e9770
            If loops < max_loops and inexact is zero, we can exit the
Packit Service 2e9770
            while-loop since it only remains to add u and v into a. */
Packit Service 2e9770
         if (inexact) {
Packit Service 2e9770
             mpfr_set_prec (res, prec);
Packit Service 2e9770
             mpfr_add (res, u, v, MPFR_RNDD); /* err <= 3 ulp in prec */
Packit Service 2e9770
         }
Packit Service 2e9770
Packit Service 2e9770
      } while (loops < max_loops && inexact != 0
Packit Service 2e9770
               && !mpfr_can_round (res, prec - 2, MPFR_RNDD, MPFR_RNDU,
Packit Service 2e9770
                                   mpfr_get_prec (a) + (rnd == MPFR_RNDN)));
Packit Service 2e9770
Packit Service 2e9770
      if (!inexact)
Packit Service 2e9770
         /* squarings were exact, neither underflow nor overflow */
Packit Service 2e9770
         inexact = mpfr_add (a, u, v, rnd);
Packit Service 2e9770
      /* if there was an overflow in Re(b)^2 or Im(b)^2 or their sum,
Packit Service 2e9770
         since the norm is larger, there is an overflow for the norm */
Packit Service 2e9770
      else if (mpfr_overflow_p ()) {
Packit Service 2e9770
         /* replace by "correctly rounded overflow" */
Packit Service 2e9770
         mpfr_set_ui (a, 1ul, MPFR_RNDN);
Packit Service 2e9770
         inexact = mpfr_mul_2ui (a, a, mpfr_get_emax (), rnd);
Packit Service 2e9770
      }
Packit Service 2e9770
      else if (mpfr_underflow_p ()) {
Packit Service 2e9770
         /* necessarily one of the squarings did underflow (otherwise their
Packit Service 2e9770
            sum could not underflow), thus one of u, v is zero. */
Packit Service 2e9770
         mpfr_exp_t emin = mpfr_get_emin ();
Packit Service 2e9770
Packit Service 2e9770
         /* Now either both u and v are zero, or u is zero and v exact,
Packit Service 2e9770
            or v is zero and u exact.
Packit Service 2e9770
            In the latter case, Im(b)^2 < 2^(emin-1).
Packit Service 2e9770
            If ulp(u) >= 2^(emin+1) and norm(b) is not exactly
Packit Service 2e9770
            representable at the target precision, then rounding u+Im(b)^2
Packit Service 2e9770
            is equivalent to rounding u+2^(emin-1).
Packit Service 2e9770
            For instance, if exp(u)>0 and the target precision is smaller
Packit Service 2e9770
            than about |emin|, the norm is not representable. To make the
Packit Service 2e9770
            scaling in the "else" case work without underflow, we test
Packit Service 2e9770
            whether exp(u) is larger than a small negative number instead.
Packit Service 2e9770
            The second case is handled analogously.                        */
Packit Service 2e9770
         if (!mpfr_zero_p (u)
Packit Service 2e9770
             && mpfr_get_exp (u) - 2 * (mpfr_exp_t) prec_u > emin
Packit Service 2e9770
             && mpfr_get_exp (u) > -10) {
Packit Service 2e9770
               mpfr_set_prec (v, MPFR_PREC_MIN);
Packit Service 2e9770
               mpfr_set_ui_2exp (v, 1, emin - 1, MPFR_RNDZ);
Packit Service 2e9770
               inexact = mpfr_add (a, u, v, rnd);
Packit Service 2e9770
         }
Packit Service 2e9770
         else if (!mpfr_zero_p (v)
Packit Service 2e9770
             && mpfr_get_exp (v) - 2 * (mpfr_exp_t) prec_v > emin
Packit Service 2e9770
             && mpfr_get_exp (v) > -10) {
Packit Service 2e9770
               mpfr_set_prec (u, MPFR_PREC_MIN);
Packit Service 2e9770
               mpfr_set_ui_2exp (u, 1, emin - 1, MPFR_RNDZ);
Packit Service 2e9770
               inexact = mpfr_add (a, u, v, rnd);
Packit Service 2e9770
         }
Packit Service 2e9770
         else {
Packit Service 2e9770
            unsigned long int scale, exp_re, exp_im;
Packit Service 2e9770
            int inex_underflow;
Packit Service 2e9770
Packit Service 2e9770
            /* scale the input to an average exponent close to 0 */
Packit Service 2e9770
            exp_re = (unsigned long int) (-mpfr_get_exp (mpc_realref (b)));
Packit Service 2e9770
            exp_im = (unsigned long int) (-mpfr_get_exp (mpc_imagref (b)));
Packit Service 2e9770
            scale = exp_re / 2 + exp_im / 2 + (exp_re % 2 + exp_im % 2) / 2;
Packit Service 2e9770
               /* (exp_re + exp_im) / 2, computed in a way avoiding
Packit Service 2e9770
                  integer overflow                                  */
Packit Service 2e9770
            if (mpfr_zero_p (u)) {
Packit Service 2e9770
               /* recompute the scaled value exactly */
Packit Service 2e9770
               mpfr_mul_2ui (u, mpc_realref (b), scale, MPFR_RNDN);
Packit Service 2e9770
               mpfr_sqr (u, u, MPFR_RNDN);
Packit Service 2e9770
            }
Packit Service 2e9770
            else /* just scale */
Packit Service 2e9770
               mpfr_mul_2ui (u, u, 2*scale, MPFR_RNDN);
Packit Service 2e9770
            if (mpfr_zero_p (v)) {
Packit Service 2e9770
               mpfr_mul_2ui (v, mpc_imagref (b), scale, MPFR_RNDN);
Packit Service 2e9770
               mpfr_sqr (v, v, MPFR_RNDN);
Packit Service 2e9770
            }
Packit Service 2e9770
            else
Packit Service 2e9770
               mpfr_mul_2ui (v, v, 2*scale, MPFR_RNDN);
Packit Service 2e9770
Packit Service 2e9770
            inexact = mpfr_add (a, u, v, rnd);
Packit Service 2e9770
            mpfr_clear_underflow ();
Packit Service 2e9770
            inex_underflow = mpfr_div_2ui (a, a, 2*scale, rnd);
Packit Service 2e9770
            if (mpfr_underflow_p ())
Packit Service 2e9770
               inexact = inex_underflow;
Packit Service 2e9770
         }
Packit Service 2e9770
      }
Packit Service 2e9770
      else /* no problems, ternary value due to mpfr_can_round trick */
Packit Service 2e9770
         inexact = mpfr_set (a, res, rnd);
Packit Service 2e9770
Packit Service 2e9770
      /* restore underflow and overflow flags from MPFR */
Packit Service 2e9770
      if (saved_underflow)
Packit Service 2e9770
        mpfr_set_underflow ();
Packit Service 2e9770
      if (saved_overflow)
Packit Service 2e9770
        mpfr_set_overflow ();
Packit Service 2e9770
Packit Service 2e9770
      mpfr_clear (u);
Packit Service 2e9770
      mpfr_clear (v);
Packit Service 2e9770
      mpfr_clear (res);
Packit Service 2e9770
   }
Packit Service 2e9770
Packit Service 2e9770
   return inexact;
Packit Service 2e9770
}