Blame mpf/mul_2exp.c

Packit 5c3484
/* mpf_mul_2exp -- Multiply a float by 2^n.
Packit 5c3484
Packit 5c3484
Copyright 1993, 1994, 1996, 2000-2002, 2004 Free Software Foundation, Inc.
Packit 5c3484
Packit 5c3484
This file is part of the GNU MP Library.
Packit 5c3484
Packit 5c3484
The GNU MP Library is free software; you can redistribute it and/or modify
Packit 5c3484
it under the terms of either:
Packit 5c3484
Packit 5c3484
  * the GNU Lesser General Public License as published by the Free
Packit 5c3484
    Software Foundation; either version 3 of the License, or (at your
Packit 5c3484
    option) any later version.
Packit 5c3484
Packit 5c3484
or
Packit 5c3484
Packit 5c3484
  * the GNU General Public License as published by the Free Software
Packit 5c3484
    Foundation; either version 2 of the License, or (at your option) any
Packit 5c3484
    later version.
Packit 5c3484
Packit 5c3484
or both in parallel, as here.
Packit 5c3484
Packit 5c3484
The GNU MP Library is distributed in the hope that it will be useful, but
Packit 5c3484
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 5c3484
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
Packit 5c3484
for more details.
Packit 5c3484
Packit 5c3484
You should have received copies of the GNU General Public License and the
Packit 5c3484
GNU Lesser General Public License along with the GNU MP Library.  If not,
Packit 5c3484
see https://www.gnu.org/licenses/.  */
Packit 5c3484
Packit 5c3484
#include "gmp.h"
Packit 5c3484
#include "gmp-impl.h"
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* Multiples of GMP_NUMB_BITS in exp simply mean an amount added to EXP(u)
Packit 5c3484
   to set EXP(r).  The remainder exp%GMP_NUMB_BITS is then a left shift for
Packit 5c3484
   the limb data.
Packit 5c3484
Packit 5c3484
   If exp%GMP_NUMB_BITS == 0 then there's no shifting, we effectively just
Packit 5c3484
   do an mpz_set with changed EXP(r).  Like mpz_set we take prec+1 limbs in
Packit 5c3484
   this case.  Although just prec would suffice, it's nice to have
Packit 5c3484
   mpf_mul_2exp with exp==0 come out the same as mpz_set.
Packit 5c3484
Packit 5c3484
   When shifting we take up to prec many limbs from the input.  Our shift is
Packit 5c3484
   cy = mpn_lshift (PTR(r), PTR(u)+k, size, ...), where k is the number of
Packit 5c3484
   low limbs dropped from u, and the carry out is stored to PTR(r)[size].
Packit 5c3484
Packit 5c3484
   It may be noted that the low limb PTR(r)[0] doesn't incorporate bits from
Packit 5c3484
   PTR(u)[k-1] (when k>=1 makes that limb available).  Taking just prec
Packit 5c3484
   limbs from the input (with the high non-zero) is enough bits for the
Packit 5c3484
   application requested precision, there's no need for extra work.
Packit 5c3484
Packit 5c3484
   If r==u the shift will have overlapping operands.  When k==0 (ie. when
Packit 5c3484
   usize <= prec), the overlap is supported by lshift (ie. dst == src).
Packit 5c3484
Packit 5c3484
   But when r==u and k>=1 (ie. usize > prec), we would have an invalid
Packit 5c3484
   overlap (ie. mpn_lshift (rp, rp+k, ...)).  In this case we must instead
Packit 5c3484
   use mpn_rshift (PTR(r)+1, PTR(u)+k, size, NUMB-shift) with the carry out
Packit 5c3484
   stored to PTR(r)[0].  An rshift by NUMB-shift bits like this gives
Packit 5c3484
   identical data, it's just its overlap restrictions which differ.
Packit 5c3484
Packit 5c3484
   Enhancements:
Packit 5c3484
Packit 5c3484
   The way mpn_lshift is used means successive mpf_mul_2exp calls on the
Packit 5c3484
   same operand will accumulate low zero limbs, until prec+1 limbs is
Packit 5c3484
   reached.  This is wasteful for subsequent operations.  When abs_usize <=
Packit 5c3484
   prec, we should test the low exp%GMP_NUMB_BITS many bits of PTR(u)[0],
Packit 5c3484
   ie. those which would be shifted out by an mpn_rshift.  If they're zero
Packit 5c3484
   then use that mpn_rshift.  */
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
mpf_mul_2exp (mpf_ptr r, mpf_srcptr u, mp_bitcnt_t exp)
Packit 5c3484
{
Packit 5c3484
  mp_srcptr up;
Packit 5c3484
  mp_ptr rp = r->_mp_d;
Packit 5c3484
  mp_size_t usize;
Packit 5c3484
  mp_size_t abs_usize;
Packit 5c3484
  mp_size_t prec = r->_mp_prec;
Packit 5c3484
  mp_exp_t uexp = u->_mp_exp;
Packit 5c3484
Packit 5c3484
  usize = u->_mp_size;
Packit 5c3484
Packit 5c3484
  if (UNLIKELY (usize == 0))
Packit 5c3484
    {
Packit 5c3484
      r->_mp_size = 0;
Packit 5c3484
      r->_mp_exp = 0;
Packit 5c3484
      return;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  abs_usize = ABS (usize);
Packit 5c3484
  up = u->_mp_d;
Packit 5c3484
Packit 5c3484
  if (exp % GMP_NUMB_BITS == 0)
Packit 5c3484
    {
Packit 5c3484
      prec++;			/* retain more precision here as we don't need
Packit 5c3484
				   to account for carry-out here */
Packit 5c3484
      if (abs_usize > prec)
Packit 5c3484
	{
Packit 5c3484
	  up += abs_usize - prec;
Packit 5c3484
	  abs_usize = prec;
Packit 5c3484
	}
Packit 5c3484
      if (rp != up)
Packit 5c3484
	MPN_COPY_INCR (rp, up, abs_usize);
Packit 5c3484
      r->_mp_exp = uexp + exp / GMP_NUMB_BITS;
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      mp_limb_t cy_limb;
Packit 5c3484
      mp_size_t adj;
Packit 5c3484
      if (abs_usize > prec)
Packit 5c3484
	{
Packit 5c3484
	  up += abs_usize - prec;
Packit 5c3484
	  abs_usize = prec;
Packit 5c3484
	  /* Use mpn_rshift since mpn_lshift operates downwards, and we
Packit 5c3484
	     therefore would clobber part of U before using that part, in case
Packit 5c3484
	     R is the same variable as U.  */
Packit 5c3484
	  cy_limb = mpn_rshift (rp + 1, up, abs_usize,
Packit 5c3484
				GMP_NUMB_BITS - exp % GMP_NUMB_BITS);
Packit 5c3484
	  rp[0] = cy_limb;
Packit 5c3484
	  adj = rp[abs_usize] != 0;
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  cy_limb = mpn_lshift (rp, up, abs_usize, exp % GMP_NUMB_BITS);
Packit 5c3484
	  rp[abs_usize] = cy_limb;
Packit 5c3484
	  adj = cy_limb != 0;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      abs_usize += adj;
Packit 5c3484
      r->_mp_exp = uexp + exp / GMP_NUMB_BITS + adj;
Packit 5c3484
    }
Packit 5c3484
  r->_mp_size = usize >= 0 ? abs_usize : -abs_usize;
Packit 5c3484
}