Blame src/agm.c

Packit Service 8591b6
/* mpfr_agm -- arithmetic-geometric mean of two floating-point numbers
Packit Service 8591b6
Packit Service 8591b6
Copyright 1999-2017 Free Software Foundation, Inc.
Packit Service 8591b6
Contributed by the AriC and Caramba projects, INRIA.
Packit Service 8591b6
Packit Service 8591b6
This file is part of the GNU MPFR Library.
Packit Service 8591b6
Packit Service 8591b6
The GNU MPFR Library is free software; you can redistribute it and/or modify
Packit Service 8591b6
it under the terms of the GNU Lesser General Public License as published by
Packit Service 8591b6
the Free Software Foundation; either version 3 of the License, or (at your
Packit Service 8591b6
option) any later version.
Packit Service 8591b6
Packit Service 8591b6
The GNU MPFR Library is distributed in the hope that it will be useful, but
Packit Service 8591b6
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit Service 8591b6
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit Service 8591b6
License for more details.
Packit Service 8591b6
Packit Service 8591b6
You should have received a copy of the GNU Lesser General Public License
Packit Service 8591b6
along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
Packit Service 8591b6
http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
Packit Service 8591b6
51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
Packit Service 8591b6
Packit Service 8591b6
#define MPFR_NEED_LONGLONG_H
Packit Service 8591b6
#include "mpfr-impl.h"
Packit Service 8591b6
Packit Service 8591b6
/* agm(x,y) is between x and y, so we don't need to save exponent range */
Packit Service 8591b6
int
Packit Service 8591b6
mpfr_agm (mpfr_ptr r, mpfr_srcptr op2, mpfr_srcptr op1, mpfr_rnd_t rnd_mode)
Packit Service 8591b6
{
Packit Service 8591b6
  int compare, inexact;
Packit Service 8591b6
  mp_size_t s;
Packit Service 8591b6
  mpfr_prec_t p, q;
Packit Service 8591b6
  mp_limb_t *up, *vp, *ufp, *vfp;
Packit Service 8591b6
  mpfr_t u, v, uf, vf, sc1, sc2;
Packit Service 8591b6
  mpfr_exp_t scaleop = 0, scaleit;
Packit Service 8591b6
  unsigned long n; /* number of iterations */
Packit Service 8591b6
  MPFR_ZIV_DECL (loop);
Packit Service 8591b6
  MPFR_TMP_DECL(marker);
Packit Service 8591b6
  MPFR_SAVE_EXPO_DECL (expo);
Packit Service 8591b6
Packit Service 8591b6
  MPFR_LOG_FUNC
Packit Service 8591b6
    (("op2[%Pu]=%.*Rg op1[%Pu]=%.*Rg rnd=%d",
Packit Service 8591b6
      mpfr_get_prec (op2), mpfr_log_prec, op2,
Packit Service 8591b6
      mpfr_get_prec (op1), mpfr_log_prec, op1, rnd_mode),
Packit Service 8591b6
     ("r[%Pu]=%.*Rg inexact=%d",
Packit Service 8591b6
      mpfr_get_prec (r), mpfr_log_prec, r, inexact));
Packit Service 8591b6
Packit Service 8591b6
  /* Deal with special values */
Packit Service 8591b6
  if (MPFR_ARE_SINGULAR (op1, op2))
Packit Service 8591b6
    {
Packit Service 8591b6
      /* If a or b is NaN, the result is NaN */
Packit Service 8591b6
      if (MPFR_IS_NAN(op1) || MPFR_IS_NAN(op2))
Packit Service 8591b6
        {
Packit Service 8591b6
          MPFR_SET_NAN(r);
Packit Service 8591b6
          MPFR_RET_NAN;
Packit Service 8591b6
        }
Packit Service 8591b6
      /* now one of a or b is Inf or 0 */
Packit Service 8591b6
      /* If a and b is +Inf, the result is +Inf.
Packit Service 8591b6
         Otherwise if a or b is -Inf or 0, the result is NaN */
Packit Service 8591b6
      else if (MPFR_IS_INF(op1) || MPFR_IS_INF(op2))
Packit Service 8591b6
        {
Packit Service 8591b6
          if (MPFR_IS_STRICTPOS(op1) && MPFR_IS_STRICTPOS(op2))
Packit Service 8591b6
            {
Packit Service 8591b6
              MPFR_SET_INF(r);
Packit Service 8591b6
              MPFR_SET_SAME_SIGN(r, op1);
Packit Service 8591b6
              MPFR_RET(0); /* exact */
Packit Service 8591b6
            }
Packit Service 8591b6
          else
Packit Service 8591b6
            {
Packit Service 8591b6
              MPFR_SET_NAN(r);
Packit Service 8591b6
              MPFR_RET_NAN;
Packit Service 8591b6
            }
Packit Service 8591b6
        }
Packit Service 8591b6
      else /* a and b are neither NaN nor Inf, and one is zero */
Packit Service 8591b6
        {  /* If a or b is 0, the result is +0 since a sqrt is positive */
Packit Service 8591b6
          MPFR_ASSERTD (MPFR_IS_ZERO (op1) || MPFR_IS_ZERO (op2));
Packit Service 8591b6
          MPFR_SET_POS (r);
Packit Service 8591b6
          MPFR_SET_ZERO (r);
Packit Service 8591b6
          MPFR_RET (0); /* exact */
Packit Service 8591b6
        }
Packit Service 8591b6
    }
Packit Service 8591b6
Packit Service 8591b6
  /* If a or b is negative (excluding -Infinity), the result is NaN */
Packit Service 8591b6
  if (MPFR_UNLIKELY(MPFR_IS_NEG(op1) || MPFR_IS_NEG(op2)))
Packit Service 8591b6
    {
Packit Service 8591b6
      MPFR_SET_NAN(r);
Packit Service 8591b6
      MPFR_RET_NAN;
Packit Service 8591b6
    }
Packit Service 8591b6
Packit Service 8591b6
  /* Precision of the following calculus */
Packit Service 8591b6
  q = MPFR_PREC(r);
Packit Service 8591b6
  p = q + MPFR_INT_CEIL_LOG2(q) + 15;
Packit Service 8591b6
  MPFR_ASSERTD (p >= 7); /* see algorithms.tex */
Packit Service 8591b6
  s = MPFR_PREC2LIMBS (p);
Packit Service 8591b6
Packit Service 8591b6
  /* b (op2) and a (op1) are the 2 operands but we want b >= a */
Packit Service 8591b6
  compare = mpfr_cmp (op1, op2);
Packit Service 8591b6
  if (MPFR_UNLIKELY( compare == 0 ))
Packit Service 8591b6
    return mpfr_set (r, op1, rnd_mode);
Packit Service 8591b6
  else if (compare > 0)
Packit Service 8591b6
    {
Packit Service 8591b6
      mpfr_srcptr t = op1;
Packit Service 8591b6
      op1 = op2;
Packit Service 8591b6
      op2 = t;
Packit Service 8591b6
    }
Packit Service 8591b6
Packit Service 8591b6
  /* Now b (=op2) > a (=op1) */
Packit Service 8591b6
Packit Service 8591b6
  MPFR_SAVE_EXPO_MARK (expo);
Packit Service 8591b6
Packit Service 8591b6
  MPFR_TMP_MARK(marker);
Packit Service 8591b6
Packit Service 8591b6
  /* Main loop */
Packit Service 8591b6
  MPFR_ZIV_INIT (loop, p);
Packit Service 8591b6
  for (;;)
Packit Service 8591b6
    {
Packit Service 8591b6
      mpfr_prec_t eq;
Packit Service 8591b6
      unsigned long err = 0;  /* must be set to 0 at each Ziv iteration */
Packit Service 8591b6
      MPFR_BLOCK_DECL (flags);
Packit Service 8591b6
Packit Service 8591b6
      /* Init temporary vars */
Packit Service 8591b6
      MPFR_TMP_INIT (up, u, p, s);
Packit Service 8591b6
      MPFR_TMP_INIT (vp, v, p, s);
Packit Service 8591b6
      MPFR_TMP_INIT (ufp, uf, p, s);
Packit Service 8591b6
      MPFR_TMP_INIT (vfp, vf, p, s);
Packit Service 8591b6
Packit Service 8591b6
      /* Calculus of un and vn */
Packit Service 8591b6
    retry:
Packit Service 8591b6
      MPFR_BLOCK (flags,
Packit Service 8591b6
                  mpfr_mul (u, op1, op2, MPFR_RNDN);
Packit Service 8591b6
                  /* mpfr_mul(...): faster since PREC(op) < PREC(u) */
Packit Service 8591b6
                  mpfr_add (v, op1, op2, MPFR_RNDN);
Packit Service 8591b6
                  /* mpfr_add with !=prec is still good */);
Packit Service 8591b6
      if (MPFR_UNLIKELY (MPFR_OVERFLOW (flags) || MPFR_UNDERFLOW (flags)))
Packit Service 8591b6
        {
Packit Service 8591b6
          mpfr_exp_t e1 , e2;
Packit Service 8591b6
Packit Service 8591b6
          MPFR_ASSERTN (scaleop == 0);
Packit Service 8591b6
          e1 = MPFR_GET_EXP (op1);
Packit Service 8591b6
          e2 = MPFR_GET_EXP (op2);
Packit Service 8591b6
Packit Service 8591b6
          /* Let's determine scaleop to avoid an overflow/underflow. */
Packit Service 8591b6
          if (MPFR_OVERFLOW (flags))
Packit Service 8591b6
            {
Packit Service 8591b6
              /* Let's recall that emin <= e1 <= e2 <= emax.
Packit Service 8591b6
                 There has been an overflow. Thus e2 >= emax/2.
Packit Service 8591b6
                 If the mpfr_mul overflowed, then e1 + e2 > emax.
Packit Service 8591b6
                 If the mpfr_add overflowed, then e2 = emax.
Packit Service 8591b6
                 We want: (e1 + scale) + (e2 + scale) <= emax,
Packit Service 8591b6
                 i.e. scale <= (emax - e1 - e2) / 2. Let's take
Packit Service 8591b6
                 scale = min(floor((emax - e1 - e2) / 2), -1).
Packit Service 8591b6
                 This is OK, as:
Packit Service 8591b6
                 1. emin <= scale <= -1.
Packit Service 8591b6
                 2. e1 + scale >= emin. Indeed:
Packit Service 8591b6
                    * If e1 + e2 > emax, then
Packit Service 8591b6
                      e1 + scale >= e1 + (emax - e1 - e2) / 2 - 1
Packit Service 8591b6
                                 >= (emax + e1 - emax) / 2 - 1
Packit Service 8591b6
                                 >= e1 / 2 - 1 >= emin.
Packit Service 8591b6
                    * Otherwise, mpfr_mul didn't overflow, therefore
Packit Service 8591b6
                      mpfr_add overflowed and e2 = emax, so that
Packit Service 8591b6
                      e1 > emin (see restriction below).
Packit Service 8591b6
                      e1 + scale > emin - 1, thus e1 + scale >= emin.
Packit Service 8591b6
                 3. e2 + scale <= emax, since scale < 0. */
Packit Service 8591b6
              if (e1 + e2 > MPFR_EXT_EMAX)
Packit Service 8591b6
                {
Packit Service 8591b6
                  scaleop = - (((e1 + e2) - MPFR_EXT_EMAX + 1) / 2);
Packit Service 8591b6
                  MPFR_ASSERTN (scaleop < 0);
Packit Service 8591b6
                }
Packit Service 8591b6
              else
Packit Service 8591b6
                {
Packit Service 8591b6
                  /* The addition necessarily overflowed. */
Packit Service 8591b6
                  MPFR_ASSERTN (e2 == MPFR_EXT_EMAX);
Packit Service 8591b6
                  /* The case where e1 = emin and e2 = emax is not supported
Packit Service 8591b6
                     here. This would mean that the precision of e2 would be
Packit Service 8591b6
                     huge (and possibly not supported in practice anyway). */
Packit Service 8591b6
                  MPFR_ASSERTN (e1 > MPFR_EXT_EMIN);
Packit Service 8591b6
                  scaleop = -1;
Packit Service 8591b6
                }
Packit Service 8591b6
Packit Service 8591b6
            }
Packit Service 8591b6
          else  /* underflow only (in the multiplication) */
Packit Service 8591b6
            {
Packit Service 8591b6
              /* We have e1 + e2 <= emin (so, e1 <= e2 <= 0).
Packit Service 8591b6
                 We want: (e1 + scale) + (e2 + scale) >= emin + 1,
Packit Service 8591b6
                 i.e. scale >= (emin + 1 - e1 - e2) / 2. let's take
Packit Service 8591b6
                 scale = ceil((emin + 1 - e1 - e2) / 2). This is OK, as:
Packit Service 8591b6
                 1. 1 <= scale <= emax.
Packit Service 8591b6
                 2. e1 + scale >= emin + 1 >= emin.
Packit Service 8591b6
                 3. e2 + scale <= scale <= emax. */
Packit Service 8591b6
              MPFR_ASSERTN (e1 <= e2 && e2 <= 0);
Packit Service 8591b6
              scaleop = (MPFR_EXT_EMIN + 2 - e1 - e2) / 2;
Packit Service 8591b6
              MPFR_ASSERTN (scaleop > 0);
Packit Service 8591b6
            }
Packit Service 8591b6
Packit Service 8591b6
          MPFR_ALIAS (sc1, op1, MPFR_SIGN (op1), e1 + scaleop);
Packit Service 8591b6
          MPFR_ALIAS (sc2, op2, MPFR_SIGN (op2), e2 + scaleop);
Packit Service 8591b6
          op1 = sc1;
Packit Service 8591b6
          op2 = sc2;
Packit Service 8591b6
          MPFR_LOG_MSG (("Exception in pre-iteration, scale = %"
Packit Service 8591b6
                         MPFR_EXP_FSPEC "d\n", scaleop));
Packit Service 8591b6
          goto retry;
Packit Service 8591b6
        }
Packit Service 8591b6
Packit Service 8591b6
      mpfr_clear_flags ();
Packit Service 8591b6
      mpfr_sqrt (u, u, MPFR_RNDN);
Packit Service 8591b6
      mpfr_div_2ui (v, v, 1, MPFR_RNDN);
Packit Service 8591b6
Packit Service 8591b6
      scaleit = 0;
Packit Service 8591b6
      n = 1;
Packit Service 8591b6
      while (mpfr_cmp2 (u, v, &eq) != 0 && eq <= p - 2)
Packit Service 8591b6
        {
Packit Service 8591b6
          MPFR_BLOCK_DECL (flags2);
Packit Service 8591b6
Packit Service 8591b6
          MPFR_LOG_MSG (("Iteration n = %lu\n", n));
Packit Service 8591b6
Packit Service 8591b6
        retry2:
Packit Service 8591b6
          mpfr_add (vf, u, v, MPFR_RNDN);  /* No overflow? */
Packit Service 8591b6
          mpfr_div_2ui (vf, vf, 1, MPFR_RNDN);
Packit Service 8591b6
          /* See proof in algorithms.tex */
Packit Service 8591b6
          if (4*eq > p)
Packit Service 8591b6
            {
Packit Service 8591b6
              mpfr_t w;
Packit Service 8591b6
              MPFR_BLOCK_DECL (flags3);
Packit Service 8591b6
Packit Service 8591b6
              MPFR_LOG_MSG (("4*eq > p\n", 0));
Packit Service 8591b6
Packit Service 8591b6
              /* vf = V(k) */
Packit Service 8591b6
              mpfr_init2 (w, (p + 1) / 2);
Packit Service 8591b6
              MPFR_BLOCK
Packit Service 8591b6
                (flags3,
Packit Service 8591b6
                 mpfr_sub (w, v, u, MPFR_RNDN);       /* e = V(k-1)-U(k-1) */
Packit Service 8591b6
                 mpfr_sqr (w, w, MPFR_RNDN);          /* e = e^2 */
Packit Service 8591b6
                 mpfr_div_2ui (w, w, 4, MPFR_RNDN);   /* e*= (1/2)^2*1/4  */
Packit Service 8591b6
                 mpfr_div (w, w, vf, MPFR_RNDN);      /* 1/4*e^2/V(k) */
Packit Service 8591b6
                 );
Packit Service 8591b6
              if (MPFR_LIKELY (! MPFR_UNDERFLOW (flags3)))
Packit Service 8591b6
                {
Packit Service 8591b6
                  mpfr_sub (v, vf, w, MPFR_RNDN);
Packit Service 8591b6
                  err = MPFR_GET_EXP (vf) - MPFR_GET_EXP (v); /* 0 or 1 */
Packit Service 8591b6
                  mpfr_clear (w);
Packit Service 8591b6
                  break;
Packit Service 8591b6
                }
Packit Service 8591b6
              /* There has been an underflow because of the cancellation
Packit Service 8591b6
                 between V(k-1) and U(k-1). Let's use the conventional
Packit Service 8591b6
                 method. */
Packit Service 8591b6
              MPFR_LOG_MSG (("4*eq > p -> underflow\n", 0));
Packit Service 8591b6
              mpfr_clear (w);
Packit Service 8591b6
              mpfr_clear_underflow ();
Packit Service 8591b6
            }
Packit Service 8591b6
          /* U(k) increases, so that U.V can overflow (but not underflow). */
Packit Service 8591b6
          MPFR_BLOCK (flags2, mpfr_mul (uf, u, v, MPFR_RNDN););
Packit Service 8591b6
          if (MPFR_UNLIKELY (MPFR_OVERFLOW (flags2)))
Packit Service 8591b6
            {
Packit Service 8591b6
              mpfr_exp_t scale2;
Packit Service 8591b6
Packit Service 8591b6
              scale2 = - (((MPFR_GET_EXP (u) + MPFR_GET_EXP (v))
Packit Service 8591b6
                           - MPFR_EXT_EMAX + 1) / 2);
Packit Service 8591b6
              MPFR_EXP (u) += scale2;
Packit Service 8591b6
              MPFR_EXP (v) += scale2;
Packit Service 8591b6
              scaleit += scale2;
Packit Service 8591b6
              MPFR_LOG_MSG (("Overflow in iteration n = %lu, scaleit = %"
Packit Service 8591b6
                             MPFR_EXP_FSPEC "d (%" MPFR_EXP_FSPEC "d)\n",
Packit Service 8591b6
                             n, scaleit, scale2));
Packit Service 8591b6
              mpfr_clear_overflow ();
Packit Service 8591b6
              goto retry2;
Packit Service 8591b6
            }
Packit Service 8591b6
          mpfr_sqrt (u, uf, MPFR_RNDN);
Packit Service 8591b6
          mpfr_swap (v, vf);
Packit Service 8591b6
          n ++;
Packit Service 8591b6
        }
Packit Service 8591b6
Packit Service 8591b6
      MPFR_LOG_MSG (("End of iterations (n = %lu)\n", n));
Packit Service 8591b6
Packit Service 8591b6
      /* the error on v is bounded by (18n+51) ulps, or twice if there
Packit Service 8591b6
         was an exponent loss in the final subtraction */
Packit Service 8591b6
      err += MPFR_INT_CEIL_LOG2(18 * n + 51); /* 18n+51 should not overflow
Packit Service 8591b6
                                                 since n is about log(p) */
Packit Service 8591b6
      /* we should have n+2 <= 2^(p/4) [see algorithms.tex] */
Packit Service 8591b6
      if (MPFR_LIKELY (MPFR_INT_CEIL_LOG2(n + 2) <= p / 4 &&
Packit Service 8591b6
                       MPFR_CAN_ROUND (v, p - err, q, rnd_mode)))
Packit Service 8591b6
        break; /* Stop the loop */
Packit Service 8591b6
Packit Service 8591b6
      /* Next iteration */
Packit Service 8591b6
      MPFR_ZIV_NEXT (loop, p);
Packit Service 8591b6
      s = MPFR_PREC2LIMBS (p);
Packit Service 8591b6
    }
Packit Service 8591b6
  MPFR_ZIV_FREE (loop);
Packit Service 8591b6
Packit Service 8591b6
  if (MPFR_UNLIKELY ((__gmpfr_flags & (MPFR_FLAGS_ALL ^ MPFR_FLAGS_INEXACT))
Packit Service 8591b6
                     != 0))
Packit Service 8591b6
    {
Packit Service 8591b6
      MPFR_ASSERTN (! mpfr_overflow_p ());   /* since mpfr_clear_flags */
Packit Service 8591b6
      MPFR_ASSERTN (! mpfr_underflow_p ());  /* since mpfr_clear_flags */
Packit Service 8591b6
      MPFR_ASSERTN (! mpfr_divby0_p ());     /* since mpfr_clear_flags */
Packit Service 8591b6
      MPFR_ASSERTN (! mpfr_nanflag_p ());    /* since mpfr_clear_flags */
Packit Service 8591b6
    }
Packit Service 8591b6
Packit Service 8591b6
  /* Setting of the result */
Packit Service 8591b6
  inexact = mpfr_set (r, v, rnd_mode);
Packit Service 8591b6
  MPFR_EXP (r) -= scaleop + scaleit;
Packit Service 8591b6
Packit Service 8591b6
  /* Let's clean */
Packit Service 8591b6
  MPFR_TMP_FREE(marker);
Packit Service 8591b6
Packit Service 8591b6
  MPFR_SAVE_EXPO_FREE (expo);
Packit Service 8591b6
  /* From the definition of the AGM, underflow and overflow
Packit Service 8591b6
     are not possible. */
Packit Service 8591b6
  return mpfr_check_range (r, inexact, rnd_mode);
Packit Service 8591b6
  /* agm(u,v) can be exact for u, v rational only for u=v.
Packit Service 8591b6
     Proof (due to Nicolas Brisebarre): it suffices to consider
Packit Service 8591b6
     u=1 and v<1. Then 1/AGM(1,v) = 2F1(1/2,1/2,1;1-v^2),
Packit Service 8591b6
     and a theorem due to G.V. Chudnovsky states that for x a
Packit Service 8591b6
     non-zero algebraic number with |x|<1, then
Packit Service 8591b6
     2F1(1/2,1/2,1;x) and 2F1(-1/2,1/2,1;x) are algebraically
Packit Service 8591b6
     independent over Q. */
Packit Service 8591b6
}