Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / libgmp / mpf / set_str.c
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 (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
6
7 This file is part of the GNU MP Library.
8
9 The GNU MP Library is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Library General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or (at your
12 option) any later version.
13
14 The GNU MP Library is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
17 License for more details.
18
19 You should have received a copy of the GNU Library General Public License
20 along with the GNU MP Library; see the file COPYING.LIB.  If not, write to
21 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22 MA 02111-1307, USA. */
23
24 #include <string.h>
25 #include <ctype.h>
26 #include "gmp.h"
27 #include "gmp-impl.h"
28 #include "longlong.h"
29
30 long int strtol _PROTO ((const char *, char **ptr, int));
31
32 static int
33 digit_value_in_base (c, base)
34      int c;
35      int base;
36 {
37   int digit;
38
39   if (isdigit (c))
40     digit = c - '0';
41   else if (islower (c))
42     digit = c - 'a' + 10;
43   else if (isupper (c))
44     digit = c - 'A' + 10;
45   else
46     return -1;
47
48   if (digit < base)
49     return digit;
50   return -1;
51 }
52
53 int
54 #if __STDC__
55 mpf_set_str (mpf_ptr x, const char *str, int base)
56 #else
57 mpf_set_str (x, str, base)
58      mpf_ptr x;
59      char *str;
60      int base;
61 #endif
62 {
63   size_t str_size;
64   char *s, *begs;
65   size_t i;
66   mp_size_t xsize;
67   int c;
68   int negative;
69   char *dotpos = 0;
70   int expflag;
71   int decimal_exponent_flag;
72   TMP_DECL (marker);
73
74   TMP_MARK (marker);
75
76   c = *str;
77
78   /* Skip whitespace.  */
79   while (isspace (c))
80     c = *++str;
81
82   negative = 0;
83   if (c == '-')
84     {
85       negative = 1;
86       c = *++str;
87     }
88
89   decimal_exponent_flag = base < 0;
90   base = ABS (base);
91
92   if (digit_value_in_base (c, base == 0 ? 10 : base) < 0)
93     return -1;                  /* error if no digits */
94
95   /* If BASE is 0, try to find out the base by looking at the initial
96      characters.  */
97   if (base == 0)
98     {
99       base = 10;
100 #if 0
101       if (c == '0')
102         {
103           base = 8;
104           c = *++str;
105           if (c == 'x' || c == 'X')
106             base = 16;
107         }
108 #endif
109     }
110
111   expflag = 0;
112   str_size = strlen (str);
113   for (i = 0; i < str_size; i++)
114     {
115       c = str[i];
116       if (c == '@' || (base <= 10 && (c == 'e' || c == 'E')))
117         {
118           expflag = 1;
119           str_size = i;
120           break;
121         }
122
123     }
124
125   s = begs = (char *) TMP_ALLOC (str_size + 1);
126
127   for (i = 0; i < str_size; i++)
128     {
129       c = *str;
130       if (!isspace (c))
131         {
132           int dig;
133
134           if (c == '.')
135             {
136               if (dotpos != 0)
137                 {
138                   TMP_FREE (marker);
139                   return -1;
140                 }
141               dotpos = s;
142             }
143           else
144             {
145               dig = digit_value_in_base (c, base);
146               if (dig < 0)
147                 {
148                   TMP_FREE (marker);
149                   return -1;
150                 }
151               *s++ = dig;
152             }
153         }
154       c = *++str;
155     }
156
157   str_size = s - begs;
158
159   xsize = str_size / __mp_bases[base].chars_per_limb + 2;
160   {
161     long exp_in_base;
162     mp_size_t rsize, msize;
163     int cnt, i;
164     mp_ptr mp, xp, tp, rp;
165     mp_limb_t cy;
166     mp_exp_t exp_in_limbs;
167     mp_size_t prec = x->_mp_prec;
168     int divflag;
169     mp_size_t xxx = 0;
170
171     mp = (mp_ptr) TMP_ALLOC (xsize * BYTES_PER_MP_LIMB);
172     msize = mpn_set_str (mp, (unsigned char *) begs, str_size, base);
173
174     if (msize == 0)
175       {
176         x->_mp_size = 0;
177         x->_mp_exp = 0;
178         TMP_FREE (marker);
179         return 0;
180       }
181
182     if (expflag != 0)
183       exp_in_base = strtol (str + 1, (char **) 0,
184                             decimal_exponent_flag ? 10 : base);
185     else
186       exp_in_base = 0;
187     if (dotpos != 0)
188       exp_in_base -= s - dotpos;
189     divflag = exp_in_base < 0;
190     exp_in_base = ABS (exp_in_base);
191
192     if (exp_in_base == 0)
193       {
194         MPN_COPY (x->_mp_d, mp, msize);
195         x->_mp_size = negative ? -msize : msize;
196         x->_mp_exp = msize;
197         TMP_FREE (marker);
198         return 0;
199       }
200
201 #if 1
202     rsize = (((mp_size_t) (exp_in_base / __mp_bases[base].chars_per_bit_exactly))
203              / BITS_PER_MP_LIMB + 3);
204 #else
205     count_leading_zeros (cnt, (mp_limb_t) base);
206     rsize = exp_in_base - cnt * exp_in_base / BITS_PER_MP_LIMB + 1;
207 #endif
208     rp = (mp_ptr) TMP_ALLOC (rsize * BYTES_PER_MP_LIMB);
209     tp = (mp_ptr) TMP_ALLOC (rsize * BYTES_PER_MP_LIMB);
210
211     rp[0] = base;
212     rsize = 1;
213
214     count_leading_zeros (cnt, exp_in_base);
215
216     for (i = BITS_PER_MP_LIMB - cnt - 2; i >= 0; i--)
217       {
218         mpn_mul_n (tp, rp, rp, rsize);
219         rsize = 2 * rsize;
220         rsize -= tp[rsize - 1] == 0;
221         xp = tp; tp = rp; rp = xp;
222
223         if (((exp_in_base >> i) & 1) != 0)
224           {
225             cy = mpn_mul_1 (rp, rp, rsize, (mp_limb_t) base);
226             rp[rsize] = cy;
227             rsize += cy != 0;
228           }
229       }
230
231     if (rsize > prec)
232       {
233         xxx += rsize - prec;
234         rp += rsize - prec;
235         rsize = prec;
236       }
237 #if 0
238     if (msize > prec)
239       {
240         xxx -= msize - prec;
241         mp += msize - prec;
242         msize = prec;
243       }
244 #endif
245     if (divflag)
246       {
247         mp_ptr qp;
248         mp_limb_t qflag;
249         mp_size_t xtra;
250         if (msize <= rsize)
251           {
252             /* Allocate extra limb for current divrem sematics. */
253             mp_ptr tmp = (mp_ptr) TMP_ALLOC ((rsize + 1) * BYTES_PER_MP_LIMB);
254             MPN_ZERO (tmp, rsize - msize);
255             MPN_COPY (tmp + rsize - msize, mp, msize);
256             mp = tmp;
257             xxx += rsize - msize;
258             msize = rsize;
259           }
260         count_leading_zeros (cnt, rp[rsize - 1]);
261         if (cnt != 0)
262           {
263             mpn_lshift (rp, rp, rsize, cnt);
264             cy = mpn_lshift (mp, mp, msize, cnt);
265             if (cy)
266               mp[msize++] = cy;
267           }
268         qp = (mp_ptr) TMP_ALLOC ((prec + 1) * BYTES_PER_MP_LIMB);
269         xtra = prec - (msize - rsize);
270         qflag = mpn_divrem (qp, xtra, mp, msize, rp, rsize);
271         qp[prec] = qflag;
272         tp = qp;
273         rsize = prec + qflag;
274         exp_in_limbs = rsize - xtra - xxx;
275       }
276     else
277       {
278         tp = (mp_ptr) TMP_ALLOC ((rsize + msize) * BYTES_PER_MP_LIMB);
279         if (rsize > msize)
280           mpn_mul (tp, rp, rsize, mp, msize);
281         else
282           mpn_mul (tp, mp, msize, rp, rsize);
283         rsize += msize;
284         rsize -= tp[rsize - 1] == 0;
285         exp_in_limbs = rsize + xxx;
286
287         if (rsize > prec)
288           {
289             xxx = rsize - prec;
290             tp += rsize - prec;
291             rsize = prec;
292             exp_in_limbs += 0;
293           }
294       }
295
296     MPN_COPY (x->_mp_d, tp, rsize);
297     x->_mp_size = negative ? -rsize : rsize;
298     x->_mp_exp = exp_in_limbs;
299     TMP_FREE (marker);
300     return 0;
301   }
302 }