github.com/aergoio/aergo@v1.3.1/libtool/src/gmp-6.1.2/mpf/get_str.c (about)

     1  /* mpf_get_str (digit_ptr, exp, base, n_digits, a) -- Convert the floating
     2     point number A to a base BASE number and store N_DIGITS raw digits at
     3     DIGIT_PTR, and the base BASE exponent in the word pointed to by EXP.  For
     4     example, the number 3.1416 would be returned as "31416" in DIGIT_PTR and
     5     1 in EXP.
     6  
     7  Copyright 1993-1997, 2000-2003, 2005, 2006, 2011, 2015 Free Software
     8  Foundation, Inc.
     9  
    10  This file is part of the GNU MP Library.
    11  
    12  The GNU MP Library is free software; you can redistribute it and/or modify
    13  it under the terms of either:
    14  
    15    * the GNU Lesser General Public License as published by the Free
    16      Software Foundation; either version 3 of the License, or (at your
    17      option) any later version.
    18  
    19  or
    20  
    21    * the GNU General Public License as published by the Free Software
    22      Foundation; either version 2 of the License, or (at your option) any
    23      later version.
    24  
    25  or both in parallel, as here.
    26  
    27  The GNU MP Library is distributed in the hope that it will be useful, but
    28  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    29  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    30  for more details.
    31  
    32  You should have received copies of the GNU General Public License and the
    33  GNU Lesser General Public License along with the GNU MP Library.  If not,
    34  see https://www.gnu.org/licenses/.  */
    35  
    36  #include <stdlib.h>		/* for NULL */
    37  #include "gmp.h"
    38  #include "gmp-impl.h"
    39  #include "longlong.h"		/* for count_leading_zeros */
    40  
    41  /* Could use some more work.
    42  
    43     1. Allocation is excessive.  Try to combine areas.  Perhaps use result
    44        string area for temp limb space?
    45     2. We generate up to two limbs of extra digits.  This is because we don't
    46        check the exact number of bits in the input operand, and from that
    47        compute an accurate exponent (variable e in the code).  It would be
    48        cleaner and probably somewhat faster to change this.
    49  */
    50  
    51  /* Compute base^exp and return the most significant prec limbs in rp[].
    52     Put the count of omitted low limbs in *ign.
    53     Return the actual size (which might be less than prec).
    54     Allocation of rp[] and the temporary tp[] should be 2*prec+2 limbs.  */
    55  static mp_size_t
    56  mpn_pow_1_highpart (mp_ptr rp, mp_size_t *ignp,
    57  		    mp_limb_t base, unsigned long exp,
    58  		    mp_size_t prec, mp_ptr tp)
    59  {
    60    mp_size_t ign;		/* counts number of ignored low limbs in r */
    61    mp_size_t off;		/* keeps track of offset where value starts */
    62    mp_ptr passed_rp = rp;
    63    mp_size_t rn;
    64    int cnt;
    65    int i;
    66  
    67    if (exp == 0)
    68      {
    69        rp[0] = 1;
    70        *ignp = 0;
    71        return 1;
    72      }
    73  
    74    rp[0] = base;
    75    rn = 1;
    76    off = 0;
    77    ign = 0;
    78    count_leading_zeros (cnt, exp);
    79    for (i = GMP_LIMB_BITS - cnt - 2; i >= 0; i--)
    80      {
    81        mpn_sqr (tp, rp + off, rn);
    82        rn = 2 * rn;
    83        rn -= tp[rn - 1] == 0;
    84        ign <<= 1;
    85  
    86        off = 0;
    87        if (rn > prec)
    88  	{
    89  	  ign += rn - prec;
    90  	  off = rn - prec;
    91  	  rn = prec;
    92  	}
    93        MP_PTR_SWAP (rp, tp);
    94  
    95        if (((exp >> i) & 1) != 0)
    96  	{
    97  	  mp_limb_t cy;
    98  	  cy = mpn_mul_1 (rp, rp + off, rn, base);
    99  	  rp[rn] = cy;
   100  	  rn += cy != 0;
   101  	  off = 0;
   102  	}
   103      }
   104  
   105    if (rn > prec)
   106      {
   107        ASSERT (rn == prec + 1);
   108  
   109        ign += rn - prec;
   110        rp += rn - prec;
   111        rn = prec;
   112      }
   113  
   114    /* With somewhat less than 50% probability, we can skip this copy.  */
   115    if (passed_rp != rp + off)
   116      MPN_COPY_INCR (passed_rp, rp + off, rn);
   117    *ignp = ign;
   118    return rn;
   119  }
   120  
   121  char *
   122  mpf_get_str (char *dbuf, mp_exp_t *exp, int base, size_t n_digits, mpf_srcptr u)
   123  {
   124    mp_exp_t ue;
   125    mp_size_t n_limbs_needed;
   126    size_t max_digits;
   127    mp_ptr up, pp, tp;
   128    mp_size_t un, pn, tn;
   129    unsigned char *tstr;
   130    mp_exp_t exp_in_base;
   131    size_t n_digits_computed;
   132    mp_size_t i;
   133    const char *num_to_text;
   134    size_t alloc_size = 0;
   135    char *dp;
   136    TMP_DECL;
   137  
   138    up = PTR(u);
   139    un = ABSIZ(u);
   140    ue = EXP(u);
   141  
   142    if (base >= 0)
   143      {
   144        num_to_text = "0123456789abcdefghijklmnopqrstuvwxyz";
   145        if (base <= 1)
   146  	base = 10;
   147        else if (base > 36)
   148  	{
   149  	  num_to_text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
   150  	  if (base > 62)
   151  	    return NULL;
   152  	}
   153      }
   154    else
   155      {
   156        base = -base;
   157        if (base <= 1)
   158  	base = 10;
   159        else if (base > 36)
   160  	return NULL;
   161        num_to_text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
   162      }
   163  
   164    MPF_SIGNIFICANT_DIGITS (max_digits, base, PREC(u));
   165    if (n_digits == 0 || n_digits > max_digits)
   166      n_digits = max_digits;
   167  
   168    if (dbuf == 0)
   169      {
   170        /* We didn't get a string from the user.  Allocate one (and return
   171  	 a pointer to it) with space for `-' and terminating null.  */
   172        alloc_size = n_digits + 2;
   173        dbuf = (char *) (*__gmp_allocate_func) (n_digits + 2);
   174      }
   175  
   176    if (un == 0)
   177      {
   178        *exp = 0;
   179        *dbuf = 0;
   180        n_digits = 0;
   181        goto done;
   182      }
   183  
   184    TMP_MARK;
   185  
   186    /* Allocate temporary digit space.  We can't put digits directly in the user
   187       area, since we generate more digits than requested.  (We allocate
   188       2 * GMP_LIMB_BITS extra bytes because of the digit block nature of the
   189       conversion.)  */
   190    tstr = (unsigned char *) TMP_ALLOC (n_digits + 2 * GMP_LIMB_BITS + 3);
   191  
   192    LIMBS_PER_DIGIT_IN_BASE (n_limbs_needed, n_digits, base);
   193  
   194    if (un > n_limbs_needed)
   195      {
   196        up += un - n_limbs_needed;
   197        un = n_limbs_needed;
   198      }
   199  
   200    TMP_ALLOC_LIMBS_2 (pp, 2 * n_limbs_needed + 4,
   201  		     tp, 2 * n_limbs_needed + 4);
   202  
   203    if (ue <= n_limbs_needed)
   204      {
   205        /* We need to multiply number by base^n to get an n_digits integer part.  */
   206        mp_size_t n_more_limbs_needed, ign, off;
   207        unsigned long e;
   208  
   209        n_more_limbs_needed = n_limbs_needed - ue;
   210        DIGITS_IN_BASE_PER_LIMB (e, n_more_limbs_needed, base);
   211  
   212        pn = mpn_pow_1_highpart (pp, &ign, (mp_limb_t) base, e, n_limbs_needed + 1, tp);
   213        if (un > pn)
   214  	mpn_mul (tp, up, un, pp, pn);	/* FIXME: mpn_mul_highpart */
   215        else
   216  	mpn_mul (tp, pp, pn, up, un);	/* FIXME: mpn_mul_highpart */
   217        tn = un + pn;
   218        tn -= tp[tn - 1] == 0;
   219        off = un - ue - ign;
   220        if (off < 0)
   221  	{
   222  	  MPN_COPY_DECR (tp - off, tp, tn);
   223  	  MPN_ZERO (tp, -off);
   224  	  tn -= off;
   225  	  off = 0;
   226  	}
   227        n_digits_computed = mpn_get_str (tstr, base, tp + off, tn - off);
   228  
   229        exp_in_base = n_digits_computed - e;
   230      }
   231    else
   232      {
   233        /* We need to divide number by base^n to get an n_digits integer part.  */
   234        mp_size_t n_less_limbs_needed, ign, off, xn;
   235        unsigned long e;
   236        mp_ptr dummyp, xp;
   237  
   238        n_less_limbs_needed = ue - n_limbs_needed;
   239        DIGITS_IN_BASE_PER_LIMB (e, n_less_limbs_needed, base);
   240  
   241        pn = mpn_pow_1_highpart (pp, &ign, (mp_limb_t) base, e, n_limbs_needed + 1, tp);
   242  
   243        xn = n_limbs_needed + (n_less_limbs_needed-ign);
   244        xp = TMP_ALLOC_LIMBS (xn);
   245        off = xn - un;
   246        MPN_ZERO (xp, off);
   247        MPN_COPY (xp + off, up, un);
   248  
   249        dummyp = TMP_ALLOC_LIMBS (pn);
   250        mpn_tdiv_qr (tp, dummyp, (mp_size_t) 0, xp, xn, pp, pn);
   251        tn = xn - pn + 1;
   252        tn -= tp[tn - 1] == 0;
   253        n_digits_computed = mpn_get_str (tstr, base, tp, tn);
   254  
   255        exp_in_base = n_digits_computed + e;
   256      }
   257  
   258    /* We should normally have computed too many digits.  Round the result
   259       at the point indicated by n_digits.  */
   260    if (n_digits_computed > n_digits)
   261      {
   262        size_t i;
   263        /* Round the result.  */
   264        if (tstr[n_digits] * 2 >= base)
   265  	{
   266  	  n_digits_computed = n_digits;
   267  	  for (i = n_digits - 1;; i--)
   268  	    {
   269  	      unsigned int x;
   270  	      x = ++(tstr[i]);
   271  	      if (x != base)
   272  		break;
   273  	      n_digits_computed--;
   274  	      if (i == 0)
   275  		{
   276  		  /* We had something like `bbbbbbb...bd', where 2*d >= base
   277  		     and `b' denotes digit with significance base - 1.
   278  		     This rounds up to `1', increasing the exponent.  */
   279  		  tstr[0] = 1;
   280  		  n_digits_computed = 1;
   281  		  exp_in_base++;
   282  		  break;
   283  		}
   284  	    }
   285  	}
   286      }
   287  
   288    /* We might have fewer digits than requested as a result of rounding above,
   289       (i.e. 0.999999 => 1.0) or because we have a number that simply doesn't
   290       need many digits in this base (e.g., 0.125 in base 10).  */
   291    if (n_digits > n_digits_computed)
   292      n_digits = n_digits_computed;
   293  
   294    /* Remove trailing 0.  There can be many zeros.  */
   295    while (n_digits != 0 && tstr[n_digits - 1] == 0)
   296      n_digits--;
   297  
   298    dp = dbuf + (SIZ(u) < 0);
   299  
   300    /* Translate to ASCII and copy to result string.  */
   301    for (i = 0; i < n_digits; i++)
   302      dp[i] = num_to_text[tstr[i]];
   303    dp[n_digits] = 0;
   304  
   305    *exp = exp_in_base;
   306  
   307    if (SIZ(u) < 0)
   308      {
   309        dbuf[0] = '-';
   310        n_digits++;
   311      }
   312  
   313    TMP_FREE;
   314  
   315   done:
   316    /* If the string was alloced then resize it down to the actual space
   317       required.  */
   318    if (alloc_size != 0)
   319      {
   320        __GMP_REALLOCATE_FUNC_MAYBE_TYPE (dbuf, alloc_size, n_digits + 1, char);
   321      }
   322  
   323    return dbuf;
   324  }