Blame mpf/get_str.c

Packit 5c3484
/* mpf_get_str (digit_ptr, exp, base, n_digits, a) -- Convert the floating
Packit 5c3484
   point number A to a base BASE number and store N_DIGITS raw digits at
Packit 5c3484
   DIGIT_PTR, and the base BASE exponent in the word pointed to by EXP.  For
Packit 5c3484
   example, the number 3.1416 would be returned as "31416" in DIGIT_PTR and
Packit 5c3484
   1 in EXP.
Packit 5c3484
Packit 5c3484
Copyright 1993-1997, 2000-2003, 2005, 2006, 2011, 2015 Free Software
Packit 5c3484
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 <stdlib.h>		/* for NULL */
Packit 5c3484
#include "gmp.h"
Packit 5c3484
#include "gmp-impl.h"
Packit 5c3484
#include "longlong.h"		/* for count_leading_zeros */
Packit 5c3484
Packit 5c3484
/* Could use some more work.
Packit 5c3484
Packit 5c3484
   1. Allocation is excessive.  Try to combine areas.  Perhaps use result
Packit 5c3484
      string area for temp limb space?
Packit 5c3484
   2. We generate up to two limbs of extra digits.  This is because we don't
Packit 5c3484
      check the exact number of bits in the input operand, and from that
Packit 5c3484
      compute an accurate exponent (variable e in the code).  It would be
Packit 5c3484
      cleaner and probably somewhat faster to change this.
Packit 5c3484
*/
Packit 5c3484
Packit 5c3484
/* Compute base^exp and return the most significant prec limbs in rp[].
Packit 5c3484
   Put the count of omitted low limbs in *ign.
Packit 5c3484
   Return the actual size (which might be less than prec).
Packit 5c3484
   Allocation of rp[] and the temporary tp[] should be 2*prec+2 limbs.  */
Packit 5c3484
static mp_size_t
Packit 5c3484
mpn_pow_1_highpart (mp_ptr rp, mp_size_t *ignp,
Packit 5c3484
		    mp_limb_t base, unsigned long exp,
Packit 5c3484
		    mp_size_t prec, mp_ptr tp)
Packit 5c3484
{
Packit 5c3484
  mp_size_t ign;		/* counts number of ignored low limbs in r */
Packit 5c3484
  mp_size_t off;		/* keeps track of offset where value starts */
Packit 5c3484
  mp_ptr passed_rp = rp;
Packit 5c3484
  mp_size_t rn;
Packit 5c3484
  int cnt;
Packit 5c3484
  int i;
Packit 5c3484
Packit 5c3484
  if (exp == 0)
Packit 5c3484
    {
Packit 5c3484
      rp[0] = 1;
Packit 5c3484
      *ignp = 0;
Packit 5c3484
      return 1;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  rp[0] = base;
Packit 5c3484
  rn = 1;
Packit 5c3484
  off = 0;
Packit 5c3484
  ign = 0;
Packit 5c3484
  count_leading_zeros (cnt, exp);
Packit 5c3484
  for (i = GMP_LIMB_BITS - cnt - 2; i >= 0; i--)
Packit 5c3484
    {
Packit 5c3484
      mpn_sqr (tp, rp + off, rn);
Packit 5c3484
      rn = 2 * rn;
Packit 5c3484
      rn -= tp[rn - 1] == 0;
Packit 5c3484
      ign <<= 1;
Packit 5c3484
Packit 5c3484
      off = 0;
Packit 5c3484
      if (rn > prec)
Packit 5c3484
	{
Packit 5c3484
	  ign += rn - prec;
Packit 5c3484
	  off = rn - prec;
Packit 5c3484
	  rn = prec;
Packit 5c3484
	}
Packit 5c3484
      MP_PTR_SWAP (rp, tp);
Packit 5c3484
Packit 5c3484
      if (((exp >> i) & 1) != 0)
Packit 5c3484
	{
Packit 5c3484
	  mp_limb_t cy;
Packit 5c3484
	  cy = mpn_mul_1 (rp, rp + off, rn, base);
Packit 5c3484
	  rp[rn] = cy;
Packit 5c3484
	  rn += cy != 0;
Packit 5c3484
	  off = 0;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (rn > prec)
Packit 5c3484
    {
Packit 5c3484
      ASSERT (rn == prec + 1);
Packit 5c3484
Packit 5c3484
      ign += rn - prec;
Packit 5c3484
      rp += rn - prec;
Packit 5c3484
      rn = prec;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  /* With somewhat less than 50% probability, we can skip this copy.  */
Packit 5c3484
  if (passed_rp != rp + off)
Packit 5c3484
    MPN_COPY_INCR (passed_rp, rp + off, rn);
Packit 5c3484
  *ignp = ign;
Packit 5c3484
  return rn;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
char *
Packit 5c3484
mpf_get_str (char *dbuf, mp_exp_t *exp, int base, size_t n_digits, mpf_srcptr u)
Packit 5c3484
{
Packit 5c3484
  mp_exp_t ue;
Packit 5c3484
  mp_size_t n_limbs_needed;
Packit 5c3484
  size_t max_digits;
Packit 5c3484
  mp_ptr up, pp, tp;
Packit 5c3484
  mp_size_t un, pn, tn;
Packit 5c3484
  unsigned char *tstr;
Packit 5c3484
  mp_exp_t exp_in_base;
Packit 5c3484
  size_t n_digits_computed;
Packit 5c3484
  mp_size_t i;
Packit 5c3484
  const char *num_to_text;
Packit 5c3484
  size_t alloc_size = 0;
Packit 5c3484
  char *dp;
Packit 5c3484
  TMP_DECL;
Packit 5c3484
Packit 5c3484
  up = PTR(u);
Packit 5c3484
  un = ABSIZ(u);
Packit 5c3484
  ue = EXP(u);
Packit 5c3484
Packit 5c3484
  if (base >= 0)
Packit 5c3484
    {
Packit 5c3484
      num_to_text = "0123456789abcdefghijklmnopqrstuvwxyz";
Packit 5c3484
      if (base <= 1)
Packit 5c3484
	base = 10;
Packit 5c3484
      else if (base > 36)
Packit 5c3484
	{
Packit 5c3484
	  num_to_text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
Packit 5c3484
	  if (base > 62)
Packit 5c3484
	    return NULL;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      base = -base;
Packit 5c3484
      if (base <= 1)
Packit 5c3484
	base = 10;
Packit 5c3484
      else if (base > 36)
Packit 5c3484
	return NULL;
Packit 5c3484
      num_to_text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  MPF_SIGNIFICANT_DIGITS (max_digits, base, PREC(u));
Packit 5c3484
  if (n_digits == 0 || n_digits > max_digits)
Packit 5c3484
    n_digits = max_digits;
Packit 5c3484
Packit 5c3484
  if (dbuf == 0)
Packit 5c3484
    {
Packit 5c3484
      /* We didn't get a string from the user.  Allocate one (and return
Packit 5c3484
	 a pointer to it) with space for `-' and terminating null.  */
Packit 5c3484
      alloc_size = n_digits + 2;
Packit 5c3484
      dbuf = (char *) (*__gmp_allocate_func) (n_digits + 2);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (un == 0)
Packit 5c3484
    {
Packit 5c3484
      *exp = 0;
Packit 5c3484
      *dbuf = 0;
Packit 5c3484
      n_digits = 0;
Packit 5c3484
      goto done;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  TMP_MARK;
Packit 5c3484
Packit 5c3484
  /* Allocate temporary digit space.  We can't put digits directly in the user
Packit 5c3484
     area, since we generate more digits than requested.  (We allocate
Packit 5c3484
     2 * GMP_LIMB_BITS extra bytes because of the digit block nature of the
Packit 5c3484
     conversion.)  */
Packit 5c3484
  tstr = (unsigned char *) TMP_ALLOC (n_digits + 2 * GMP_LIMB_BITS + 3);
Packit 5c3484
Packit 5c3484
  LIMBS_PER_DIGIT_IN_BASE (n_limbs_needed, n_digits, base);
Packit 5c3484
Packit 5c3484
  if (un > n_limbs_needed)
Packit 5c3484
    {
Packit 5c3484
      up += un - n_limbs_needed;
Packit 5c3484
      un = n_limbs_needed;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  TMP_ALLOC_LIMBS_2 (pp, 2 * n_limbs_needed + 4,
Packit 5c3484
		     tp, 2 * n_limbs_needed + 4);
Packit 5c3484
Packit 5c3484
  if (ue <= n_limbs_needed)
Packit 5c3484
    {
Packit 5c3484
      /* We need to multiply number by base^n to get an n_digits integer part.  */
Packit 5c3484
      mp_size_t n_more_limbs_needed, ign, off;
Packit 5c3484
      unsigned long e;
Packit 5c3484
Packit 5c3484
      n_more_limbs_needed = n_limbs_needed - ue;
Packit 5c3484
      DIGITS_IN_BASE_PER_LIMB (e, n_more_limbs_needed, base);
Packit 5c3484
Packit 5c3484
      pn = mpn_pow_1_highpart (pp, &ign, (mp_limb_t) base, e, n_limbs_needed + 1, tp);
Packit 5c3484
      if (un > pn)
Packit 5c3484
	mpn_mul (tp, up, un, pp, pn);	/* FIXME: mpn_mul_highpart */
Packit 5c3484
      else
Packit 5c3484
	mpn_mul (tp, pp, pn, up, un);	/* FIXME: mpn_mul_highpart */
Packit 5c3484
      tn = un + pn;
Packit 5c3484
      tn -= tp[tn - 1] == 0;
Packit 5c3484
      off = un - ue - ign;
Packit 5c3484
      if (off < 0)
Packit 5c3484
	{
Packit 5c3484
	  MPN_COPY_DECR (tp - off, tp, tn);
Packit 5c3484
	  MPN_ZERO (tp, -off);
Packit 5c3484
	  tn -= off;
Packit 5c3484
	  off = 0;
Packit 5c3484
	}
Packit 5c3484
      n_digits_computed = mpn_get_str (tstr, base, tp + off, tn - off);
Packit 5c3484
Packit 5c3484
      exp_in_base = n_digits_computed - e;
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      /* We need to divide number by base^n to get an n_digits integer part.  */
Packit 5c3484
      mp_size_t n_less_limbs_needed, ign, off, xn;
Packit 5c3484
      unsigned long e;
Packit 5c3484
      mp_ptr dummyp, xp;
Packit 5c3484
Packit 5c3484
      n_less_limbs_needed = ue - n_limbs_needed;
Packit 5c3484
      DIGITS_IN_BASE_PER_LIMB (e, n_less_limbs_needed, base);
Packit 5c3484
Packit 5c3484
      pn = mpn_pow_1_highpart (pp, &ign, (mp_limb_t) base, e, n_limbs_needed + 1, tp);
Packit 5c3484
Packit 5c3484
      xn = n_limbs_needed + (n_less_limbs_needed-ign);
Packit 5c3484
      xp = TMP_ALLOC_LIMBS (xn);
Packit 5c3484
      off = xn - un;
Packit 5c3484
      MPN_ZERO (xp, off);
Packit 5c3484
      MPN_COPY (xp + off, up, un);
Packit 5c3484
Packit 5c3484
      dummyp = TMP_ALLOC_LIMBS (pn);
Packit 5c3484
      mpn_tdiv_qr (tp, dummyp, (mp_size_t) 0, xp, xn, pp, pn);
Packit 5c3484
      tn = xn - pn + 1;
Packit 5c3484
      tn -= tp[tn - 1] == 0;
Packit 5c3484
      n_digits_computed = mpn_get_str (tstr, base, tp, tn);
Packit 5c3484
Packit 5c3484
      exp_in_base = n_digits_computed + e;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  /* We should normally have computed too many digits.  Round the result
Packit 5c3484
     at the point indicated by n_digits.  */
Packit 5c3484
  if (n_digits_computed > n_digits)
Packit 5c3484
    {
Packit 5c3484
      size_t i;
Packit 5c3484
      /* Round the result.  */
Packit 5c3484
      if (tstr[n_digits] * 2 >= base)
Packit 5c3484
	{
Packit 5c3484
	  n_digits_computed = n_digits;
Packit 5c3484
	  for (i = n_digits - 1;; i--)
Packit 5c3484
	    {
Packit 5c3484
	      unsigned int x;
Packit 5c3484
	      x = ++(tstr[i]);
Packit 5c3484
	      if (x != base)
Packit 5c3484
		break;
Packit 5c3484
	      n_digits_computed--;
Packit 5c3484
	      if (i == 0)
Packit 5c3484
		{
Packit 5c3484
		  /* We had something like `bbbbbbb...bd', where 2*d >= base
Packit 5c3484
		     and `b' denotes digit with significance base - 1.
Packit 5c3484
		     This rounds up to `1', increasing the exponent.  */
Packit 5c3484
		  tstr[0] = 1;
Packit 5c3484
		  n_digits_computed = 1;
Packit 5c3484
		  exp_in_base++;
Packit 5c3484
		  break;
Packit 5c3484
		}
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  /* We might have fewer digits than requested as a result of rounding above,
Packit 5c3484
     (i.e. 0.999999 => 1.0) or because we have a number that simply doesn't
Packit 5c3484
     need many digits in this base (e.g., 0.125 in base 10).  */
Packit 5c3484
  if (n_digits > n_digits_computed)
Packit 5c3484
    n_digits = n_digits_computed;
Packit 5c3484
Packit 5c3484
  /* Remove trailing 0.  There can be many zeros.  */
Packit 5c3484
  while (n_digits != 0 && tstr[n_digits - 1] == 0)
Packit 5c3484
    n_digits--;
Packit 5c3484
Packit 5c3484
  dp = dbuf + (SIZ(u) < 0);
Packit 5c3484
Packit 5c3484
  /* Translate to ASCII and copy to result string.  */
Packit 5c3484
  for (i = 0; i < n_digits; i++)
Packit 5c3484
    dp[i] = num_to_text[tstr[i]];
Packit 5c3484
  dp[n_digits] = 0;
Packit 5c3484
Packit 5c3484
  *exp = exp_in_base;
Packit 5c3484
Packit 5c3484
  if (SIZ(u) < 0)
Packit 5c3484
    {
Packit 5c3484
      dbuf[0] = '-';
Packit 5c3484
      n_digits++;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  TMP_FREE;
Packit 5c3484
Packit 5c3484
 done:
Packit 5c3484
  /* If the string was alloced then resize it down to the actual space
Packit 5c3484
     required.  */
Packit 5c3484
  if (alloc_size != 0)
Packit 5c3484
    {
Packit 5c3484
      __GMP_REALLOCATE_FUNC_MAYBE_TYPE (dbuf, alloc_size, n_digits + 1, char);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  return dbuf;
Packit 5c3484
}