source: trunk/third/gmp/mpf/set_str.c @ 18191

Revision 18191, 7.5 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18190, which included commits to RCS files with non-trunk default branches.
Line 
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
5Copyright 1993, 1994, 1995, 1996, 1997, 2000, 2001, 2002 Free Software
6Foundation, Inc.
7
8This file is part of the GNU MP Library.
9
10The GNU MP Library is free software; you can redistribute it and/or modify
11it under the terms of the GNU Lesser General Public License as published by
12the Free Software Foundation; either version 2.1 of the License, or (at your
13option) any later version.
14
15The GNU MP Library is distributed in the hope that it will be useful, but
16WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
18License for more details.
19
20You should have received a copy of the GNU Lesser General Public License
21along with the GNU MP Library; see the file COPYING.LIB.  If not, write to
22the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
23MA 02111-1307, USA. */
24
25#include "config.h"
26
27#include <stdlib.h>
28#include <string.h>
29#include <ctype.h>
30
31#if HAVE_LOCALE_H
32#include <locale.h>    /* for localeconv */
33#endif
34
35#include "gmp.h"
36#include "gmp-impl.h"
37#include "longlong.h"
38
39static int
40digit_value_in_base (int c, int base)
41{
42  int digit;
43
44  if (isdigit (c))
45    digit = c - '0';
46  else if (islower (c))
47    digit = c - 'a' + 10;
48  else if (isupper (c))
49    digit = c - 'A' + 10;
50  else
51    return -1;
52
53  if (digit < base)
54    return digit;
55  return -1;
56}
57
58#if HAVE_LOCALECONV
59/* Avoid memcmp for the usual case that point is one character.  Don't
60   bother with str+1,point+1,pointlen-1 since those offsets would add to the
61   code size.  */
62#define POINTCMP_FAST(c, str)                                           \
63  ((c) == point0                                                        \
64   && (pointlen == 1 || memcmp (str, point, pointlen) == 0))
65#define POINTCMP_SMALL(c, str)   (memcmp (str, point, pointlen) == 0)
66#define POINTLEN                 (pointlen)
67#else
68#define POINTCMP_FAST(c, str)    ((c) == '.')
69#define POINTCMP_SMALL(c, str)   ((c) == '.')
70#define POINTLEN                 1
71#endif
72
73int
74mpf_set_str (mpf_ptr x, const char *str, int base)
75{
76  size_t str_size;
77  char *s, *begs;
78  size_t i;
79  mp_size_t xsize;
80  int c;
81  int negative;
82  char *dotpos = 0;
83  int expflag;
84  int decimal_exponent_flag;
85#if HAVE_LOCALECONV
86  const char  *point = localeconv()->decimal_point;
87  size_t      pointlen = strlen (point);
88  int         point0 = point[0];
89#endif
90  TMP_DECL (marker);
91
92  TMP_MARK (marker);
93
94  c = *str;
95
96  /* Skip whitespace.  */
97  while (isspace (c))
98    c = *++str;
99
100  negative = 0;
101  if (c == '-')
102    {
103      negative = 1;
104      c = *++str;
105    }
106
107  decimal_exponent_flag = base < 0;
108  base = ABS (base);
109
110  /* require at least one digit, possibly after an initial decimal point */
111  if (digit_value_in_base (c, base == 0 ? 10 : base) < 0)
112    {
113      if (! POINTCMP_SMALL (c, str))
114        return -1;
115      if (digit_value_in_base (str[POINTLEN], base == 0 ? 10 : base) < 0)
116        return -1;
117    }
118
119  /* If BASE is 0, try to find out the base by looking at the initial
120     characters.  */
121  if (base == 0)
122    {
123      base = 10;
124#if 0
125      if (c == '0')
126        {
127          base = 8;
128          if (str[1] == 'x' || str[1] == 'X')
129            {
130              base = 16;
131              str += 2;
132              c = *str;
133            }
134        }
135#endif
136    }
137
138  expflag = 0;
139  str_size = strlen (str);
140  for (i = 0; i < str_size; i++)
141    {
142      c = str[i];
143      if (c == '@' || (base <= 10 && (c == 'e' || c == 'E')))
144        {
145          expflag = 1;
146          str_size = i;
147          break;
148        }
149
150    }
151
152  s = begs = (char *) TMP_ALLOC (str_size + 1);
153
154  for (i = 0; i < str_size; i++)
155    {
156      c = *str;
157      if (!isspace (c))
158        {
159          int dig;
160
161          if (POINTCMP_FAST (c, str))
162            {
163              if (dotpos != 0)
164                {
165                  TMP_FREE (marker);
166                  return -1;
167                }
168              dotpos = s;
169              str += POINTLEN - 1;
170              i += POINTLEN - 1;
171            }
172          else
173            {
174              dig = digit_value_in_base (c, base);
175              if (dig < 0)
176                {
177                  TMP_FREE (marker);
178                  return -1;
179                }
180              *s++ = dig;
181            }
182        }
183      c = *++str;
184    }
185
186  str_size = s - begs;
187
188  xsize = str_size / __mp_bases[base].chars_per_limb + 2;
189  {
190    long exp_in_base;
191    mp_size_t ralloc, rsize, msize;
192    int cnt, i;
193    mp_ptr mp, tp, rp;
194    mp_exp_t exp_in_limbs;
195    mp_size_t prec = x->_mp_prec + 1;
196    int divflag;
197    mp_size_t madj, radj;
198
199    mp = (mp_ptr) TMP_ALLOC (xsize * BYTES_PER_MP_LIMB);
200    msize = mpn_set_str (mp, (unsigned char *) begs, str_size, base);
201
202    if (msize == 0)
203      {
204        x->_mp_size = 0;
205        x->_mp_exp = 0;
206        TMP_FREE (marker);
207        return 0;
208      }
209
210    madj = 0;
211    /* Ignore excess limbs in MP,MSIZE.  */
212    if (msize > prec)
213      {
214        madj = msize - prec;
215        mp += msize - prec;
216        msize = prec;
217      }
218
219    if (expflag != 0)
220      exp_in_base = strtol (str + 1, (char **) 0,
221                            decimal_exponent_flag ? 10 : base);
222    else
223      exp_in_base = 0;
224    if (dotpos != 0)
225      exp_in_base -= s - dotpos;
226    divflag = exp_in_base < 0;
227    exp_in_base = ABS (exp_in_base);
228
229    if (exp_in_base == 0)
230      {
231        MPN_COPY (x->_mp_d, mp, msize);
232        x->_mp_size = negative ? -msize : msize;
233        x->_mp_exp = msize + madj;
234        TMP_FREE (marker);
235        return 0;
236      }
237
238    radj = 0;                   /* counts number of ignored low limbs in r */
239
240    ralloc = 2 * (prec + 1);
241    rp = (mp_ptr) TMP_ALLOC (ralloc * BYTES_PER_MP_LIMB);
242    tp = (mp_ptr) TMP_ALLOC (ralloc * BYTES_PER_MP_LIMB);
243
244    rp[0] = base;
245    rsize = 1;
246    count_leading_zeros (cnt, exp_in_base);
247    for (i = GMP_LIMB_BITS - cnt - 2; i >= 0; i--)
248      {
249        mpn_sqr_n (tp, rp, rsize);
250        rsize = 2 * rsize;
251        rsize -= tp[rsize - 1] == 0;
252        radj <<= 1;
253
254        if (rsize > prec)
255          {
256            radj += rsize - prec;
257            MPN_COPY_INCR (rp, tp + rsize - prec, prec);
258            rsize = prec;
259          }
260        else
261          MP_PTR_SWAP (rp, tp);
262
263        if (((exp_in_base >> i) & 1) != 0)
264          {
265            mp_limb_t cy;
266            cy = mpn_mul_1 (rp, rp, rsize, (mp_limb_t) base);
267            rp[rsize] = cy;
268            rsize += cy != 0;
269          }
270      }
271
272    if (rsize > prec)
273      {
274        radj += rsize - prec;
275        rp += rsize - prec;
276        rsize = prec;
277      }
278
279    if (divflag)
280      {
281        mp_ptr qp;
282        mp_limb_t qlimb;
283        if (msize < rsize)
284          {
285            /* Pad out MP,MSIZE for current divrem semantics.  */
286            mp_ptr tmp = (mp_ptr) TMP_ALLOC ((rsize + 1) * BYTES_PER_MP_LIMB);
287            MPN_ZERO (tmp, rsize - msize);
288            MPN_COPY (tmp + rsize - msize, mp, msize);
289            mp = tmp;
290            madj -= rsize - msize;
291            msize = rsize;
292          }
293        if ((rp[rsize - 1] & GMP_NUMB_HIGHBIT) == 0)
294          {
295            mp_limb_t cy;
296            count_leading_zeros (cnt, rp[rsize - 1]);
297            cnt -= GMP_NAIL_BITS;
298            mpn_lshift (rp, rp, rsize, cnt);
299            cy = mpn_lshift (mp, mp, msize, cnt);
300            if (cy)
301              mp[msize++] = cy;
302          }
303
304        qp = (mp_ptr) TMP_ALLOC ((prec + 1) * BYTES_PER_MP_LIMB);
305        qlimb = mpn_divrem (qp, prec - (msize - rsize), mp, msize, rp, rsize);
306        tp = qp;
307        exp_in_limbs = qlimb + (msize - rsize) + (madj - radj);
308        rsize = prec;
309        if (qlimb != 0)
310          {
311            tp[prec] = qlimb;
312            /* Skip the least significant limb not to overrun the destination
313               variable.  */
314            tp++;
315          }
316      }
317    else
318      {
319        tp = (mp_ptr) TMP_ALLOC ((rsize + msize) * BYTES_PER_MP_LIMB);
320        if (rsize > msize)
321          mpn_mul (tp, rp, rsize, mp, msize);
322        else
323          mpn_mul (tp, mp, msize, rp, rsize);
324        rsize += msize;
325        rsize -= tp[rsize - 1] == 0;
326        exp_in_limbs = rsize + madj + radj;
327
328        if (rsize > prec)
329          {
330            tp += rsize - prec;
331            rsize = prec;
332            exp_in_limbs += 0;
333          }
334      }
335
336    MPN_COPY (x->_mp_d, tp, rsize);
337    x->_mp_size = negative ? -rsize : rsize;
338    x->_mp_exp = exp_in_limbs;
339    TMP_FREE (marker);
340    return 0;
341  }
342}
Note: See TracBrowser for help on using the repository browser.