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

     1  /* mpf_set_str (dest, string, base) -- Convert the string STRING
     2     in base BASE to a float in dest.  If BASE is zero, the leading characters
     3     of STRING is used to figure out the base.
     4  
     5  Copyright 1993-1997, 2000-2003, 2005, 2007, 2008, 2011, 2013 Free Software
     6  Foundation, Inc.
     7  
     8  This file is part of the GNU MP Library.
     9  
    10  The GNU MP Library is free software; you can redistribute it and/or modify
    11  it under the terms of either:
    12  
    13    * the GNU Lesser General Public License as published by the Free
    14      Software Foundation; either version 3 of the License, or (at your
    15      option) any later version.
    16  
    17  or
    18  
    19    * the GNU General Public License as published by the Free Software
    20      Foundation; either version 2 of the License, or (at your option) any
    21      later version.
    22  
    23  or both in parallel, as here.
    24  
    25  The GNU MP Library is distributed in the hope that it will be useful, but
    26  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    27  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    28  for more details.
    29  
    30  You should have received copies of the GNU General Public License and the
    31  GNU Lesser General Public License along with the GNU MP Library.  If not,
    32  see https://www.gnu.org/licenses/.  */
    33  
    34  /*
    35    This still needs work, as suggested by some FIXME comments.
    36    1. Don't depend on superfluous mantissa digits.
    37    2. Allocate temp space more cleverly.
    38    3. Use mpn_div_q instead of mpn_lshift+mpn_divrem.
    39  */
    40  
    41  #define _GNU_SOURCE    /* for DECIMAL_POINT in langinfo.h */
    42  
    43  #include "config.h"
    44  
    45  #include <stdlib.h>
    46  #include <string.h>
    47  #include <ctype.h>
    48  
    49  #if HAVE_LANGINFO_H
    50  #include <langinfo.h>  /* for nl_langinfo */
    51  #endif
    52  
    53  #if HAVE_LOCALE_H
    54  #include <locale.h>    /* for localeconv */
    55  #endif
    56  
    57  #include "gmp.h"
    58  #include "gmp-impl.h"
    59  #include "longlong.h"
    60  
    61  
    62  #define digit_value_tab __gmp_digit_value_tab
    63  
    64  /* Compute base^exp and return the most significant prec limbs in rp[].
    65     Put the count of omitted low limbs in *ign.
    66     Return the actual size (which might be less than prec).  */
    67  static mp_size_t
    68  mpn_pow_1_highpart (mp_ptr rp, mp_size_t *ignp,
    69  		    mp_limb_t base, mp_exp_t exp,
    70  		    mp_size_t prec, mp_ptr tp)
    71  {
    72    mp_size_t ign;		/* counts number of ignored low limbs in r */
    73    mp_size_t off;		/* keeps track of offset where value starts */
    74    mp_ptr passed_rp = rp;
    75    mp_size_t rn;
    76    int cnt;
    77    int i;
    78  
    79    rp[0] = base;
    80    rn = 1;
    81    off = 0;
    82    ign = 0;
    83    count_leading_zeros (cnt, exp);
    84    for (i = GMP_LIMB_BITS - cnt - 2; i >= 0; i--)
    85      {
    86        mpn_sqr (tp, rp + off, rn);
    87        rn = 2 * rn;
    88        rn -= tp[rn - 1] == 0;
    89        ign <<= 1;
    90  
    91        off = 0;
    92        if (rn > prec)
    93  	{
    94  	  ign += rn - prec;
    95  	  off = rn - prec;
    96  	  rn = prec;
    97  	}
    98        MP_PTR_SWAP (rp, tp);
    99  
   100        if (((exp >> i) & 1) != 0)
   101  	{
   102  	  mp_limb_t cy;
   103  	  cy = mpn_mul_1 (rp, rp + off, rn, base);
   104  	  rp[rn] = cy;
   105  	  rn += cy != 0;
   106  	  off = 0;
   107  	}
   108      }
   109  
   110    if (rn > prec)
   111      {
   112        ign += rn - prec;
   113        rp += rn - prec;
   114        rn = prec;
   115      }
   116  
   117    MPN_COPY_INCR (passed_rp, rp + off, rn);
   118    *ignp = ign;
   119    return rn;
   120  }
   121  
   122  int
   123  mpf_set_str (mpf_ptr x, const char *str, int base)
   124  {
   125    size_t str_size;
   126    char *s, *begs;
   127    size_t i, j;
   128    int c;
   129    int negative;
   130    char *dotpos = 0;
   131    const char *expptr;
   132    int exp_base;
   133    const char  *point = GMP_DECIMAL_POINT;
   134    size_t      pointlen = strlen (point);
   135    const unsigned char *digit_value;
   136    TMP_DECL;
   137  
   138    c = (unsigned char) *str;
   139  
   140    /* Skip whitespace.  */
   141    while (isspace (c))
   142      c = (unsigned char) *++str;
   143  
   144    negative = 0;
   145    if (c == '-')
   146      {
   147        negative = 1;
   148        c = (unsigned char) *++str;
   149      }
   150  
   151    /* Default base to decimal.  */
   152    if (base == 0)
   153      base = 10;
   154  
   155    exp_base = base;
   156  
   157    if (base < 0)
   158      {
   159        exp_base = 10;
   160        base = -base;
   161      }
   162  
   163    digit_value = digit_value_tab;
   164    if (base > 36)
   165      {
   166        /* For bases > 36, use the collating sequence
   167  	 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.  */
   168        digit_value += 208;
   169        if (base > 62)
   170  	return -1;		/* too large base */
   171      }
   172  
   173    /* Require at least one digit, possibly after an initial decimal point.  */
   174    if (digit_value[c] >= base)
   175      {
   176        /* not a digit, must be a decimal point */
   177        for (i = 0; i < pointlen; i++)
   178  	if (str[i] != point[i])
   179  	  return -1;
   180        if (digit_value[(unsigned char) str[pointlen]] >= base)
   181  	return -1;
   182      }
   183  
   184    /* Locate exponent part of the input.  Look from the right of the string,
   185       since the exponent is usually a lot shorter than the mantissa.  */
   186    expptr = NULL;
   187    str_size = strlen (str);
   188    for (i = str_size - 1; i > 0; i--)
   189      {
   190        c = (unsigned char) str[i];
   191        if (c == '@' || (base <= 10 && (c == 'e' || c == 'E')))
   192  	{
   193  	  expptr = str + i + 1;
   194  	  str_size = i;
   195  	  break;
   196  	}
   197      }
   198  
   199    TMP_MARK;
   200    s = begs = (char *) TMP_ALLOC (str_size + 1);
   201  
   202    /* Loop through mantissa, converting it from ASCII to raw byte values.  */
   203    for (i = 0; i < str_size; i++)
   204      {
   205        c = (unsigned char) *str;
   206        if (!isspace (c))
   207  	{
   208  	  int dig;
   209  
   210  	  for (j = 0; j < pointlen; j++)
   211  	    if (str[j] != point[j])
   212  	      goto not_point;
   213  	  if (1)
   214  	    {
   215  	      if (dotpos != 0)
   216  		{
   217  		  /* already saw a decimal point, another is invalid */
   218  		  TMP_FREE;
   219  		  return -1;
   220  		}
   221  	      dotpos = s;
   222  	      str += pointlen - 1;
   223  	      i += pointlen - 1;
   224  	    }
   225  	  else
   226  	    {
   227  	    not_point:
   228  	      dig = digit_value[c];
   229  	      if (dig >= base)
   230  		{
   231  		  TMP_FREE;
   232  		  return -1;
   233  		}
   234  	      *s++ = dig;
   235  	    }
   236  	}
   237        c = (unsigned char) *++str;
   238      }
   239  
   240    str_size = s - begs;
   241  
   242    {
   243      long exp_in_base;
   244      mp_size_t ra, ma, rn, mn;
   245      int cnt;
   246      mp_ptr mp, tp, rp;
   247      mp_exp_t exp_in_limbs;
   248      mp_size_t prec = PREC(x) + 1;
   249      int divflag;
   250      mp_size_t madj, radj;
   251  
   252  #if 0
   253      size_t n_chars_needed;
   254  
   255      /* This breaks things like 0.000...0001.  To safely ignore superfluous
   256         digits, we need to skip over leading zeros.  */
   257      /* Just consider the relevant leading digits of the mantissa.  */
   258      LIMBS_PER_DIGIT_IN_BASE (n_chars_needed, prec, base);
   259      if (str_size > n_chars_needed)
   260        str_size = n_chars_needed;
   261  #endif
   262  
   263      LIMBS_PER_DIGIT_IN_BASE (ma, str_size, base);
   264      mp = TMP_ALLOC_LIMBS (ma);
   265      mn = mpn_set_str (mp, (unsigned char *) begs, str_size, base);
   266  
   267      if (mn == 0)
   268        {
   269  	SIZ(x) = 0;
   270  	EXP(x) = 0;
   271  	TMP_FREE;
   272  	return 0;
   273        }
   274  
   275      madj = 0;
   276      /* Ignore excess limbs in MP,MSIZE.  */
   277      if (mn > prec)
   278        {
   279  	madj = mn - prec;
   280  	mp += mn - prec;
   281  	mn = prec;
   282        }
   283  
   284      if (expptr != 0)
   285        {
   286  	/* Scan and convert the exponent, in base exp_base.  */
   287  	long dig, minus, plusminus;
   288  	c = (unsigned char) *expptr;
   289  	minus = -(long) (c == '-');
   290  	plusminus = minus | -(long) (c == '+');
   291  	expptr -= plusminus;			/* conditional increment */
   292  	c = (unsigned char) *expptr++;
   293  	dig = digit_value[c];
   294  	if (dig >= exp_base)
   295  	  {
   296  	    TMP_FREE;
   297  	    return -1;
   298  	  }
   299  	exp_in_base = dig;
   300  	c = (unsigned char) *expptr++;
   301  	dig = digit_value[c];
   302  	while (dig < exp_base)
   303  	  {
   304  	    exp_in_base = exp_in_base * exp_base;
   305  	    exp_in_base += dig;
   306  	    c = (unsigned char) *expptr++;
   307  	    dig = digit_value[c];
   308  	  }
   309  	exp_in_base = (exp_in_base ^ minus) - minus; /* conditional negation */
   310        }
   311      else
   312        exp_in_base = 0;
   313      if (dotpos != 0)
   314        exp_in_base -= s - dotpos;
   315      divflag = exp_in_base < 0;
   316      exp_in_base = ABS (exp_in_base);
   317  
   318      if (exp_in_base == 0)
   319        {
   320  	MPN_COPY (PTR(x), mp, mn);
   321  	SIZ(x) = negative ? -mn : mn;
   322  	EXP(x) = mn + madj;
   323  	TMP_FREE;
   324  	return 0;
   325        }
   326  
   327      ra = 2 * (prec + 1);
   328      rp = TMP_ALLOC_LIMBS (ra);
   329      tp = TMP_ALLOC_LIMBS (ra);
   330      rn = mpn_pow_1_highpart (rp, &radj, (mp_limb_t) base, exp_in_base, prec, tp);
   331  
   332      if (divflag)
   333        {
   334  #if 0
   335  	/* FIXME: Should use mpn_div_q here.  */
   336  	...
   337  	mpn_div_q (tp, mp, mn, rp, rn, scratch);
   338  	...
   339  #else
   340  	mp_ptr qp;
   341  	mp_limb_t qlimb;
   342  	if (mn < rn)
   343  	  {
   344  	    /* Pad out MP,MSIZE for current divrem semantics.  */
   345  	    mp_ptr tmp = TMP_ALLOC_LIMBS (rn + 1);
   346  	    MPN_ZERO (tmp, rn - mn);
   347  	    MPN_COPY (tmp + rn - mn, mp, mn);
   348  	    mp = tmp;
   349  	    madj -= rn - mn;
   350  	    mn = rn;
   351  	  }
   352  	if ((rp[rn - 1] & GMP_NUMB_HIGHBIT) == 0)
   353  	  {
   354  	    mp_limb_t cy;
   355  	    count_leading_zeros (cnt, rp[rn - 1]);
   356  	    cnt -= GMP_NAIL_BITS;
   357  	    mpn_lshift (rp, rp, rn, cnt);
   358  	    cy = mpn_lshift (mp, mp, mn, cnt);
   359  	    if (cy)
   360  	      mp[mn++] = cy;
   361  	  }
   362  
   363  	qp = TMP_ALLOC_LIMBS (prec + 1);
   364  	qlimb = mpn_divrem (qp, prec - (mn - rn), mp, mn, rp, rn);
   365  	tp = qp;
   366  	exp_in_limbs = qlimb + (mn - rn) + (madj - radj);
   367  	rn = prec;
   368  	if (qlimb != 0)
   369  	  {
   370  	    tp[prec] = qlimb;
   371  	    /* Skip the least significant limb not to overrun the destination
   372  	       variable.  */
   373  	    tp++;
   374  	  }
   375  #endif
   376        }
   377      else
   378        {
   379  	tp = TMP_ALLOC_LIMBS (rn + mn);
   380  	if (rn > mn)
   381  	  mpn_mul (tp, rp, rn, mp, mn);
   382  	else
   383  	  mpn_mul (tp, mp, mn, rp, rn);
   384  	rn += mn;
   385  	rn -= tp[rn - 1] == 0;
   386  	exp_in_limbs = rn + madj + radj;
   387  
   388  	if (rn > prec)
   389  	  {
   390  	    tp += rn - prec;
   391  	    rn = prec;
   392  	    exp_in_limbs += 0;
   393  	  }
   394        }
   395  
   396      MPN_COPY (PTR(x), tp, rn);
   397      SIZ(x) = negative ? -rn : rn;
   398      EXP(x) = exp_in_limbs;
   399      TMP_FREE;
   400      return 0;
   401    }
   402  }