Add hex float support.
[gnulib.git] / lib / strtod.c
1 /* Copyright (C) 1991, 1992, 1997, 1999, 2003, 2006, 2008 Free
2    Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 #include <config.h>
18
19 #include <stdlib.h>
20
21 #include <ctype.h>
22 #include <errno.h>
23 #include <float.h>
24 #include <math.h>
25 #include <stdbool.h>
26 #include <string.h>
27
28 #include "c-ctype.h"
29
30 /* Convert NPTR to a double.  If ENDPTR is not NULL, a pointer to the
31    character after the last one used in the number is put in *ENDPTR.  */
32 double
33 strtod (const char *nptr, char **endptr)
34 {
35   const unsigned char *s;
36   bool negative = false;
37
38   /* The number so far.  */
39   double num;
40
41   bool got_dot;                 /* Found a decimal point.  */
42   bool got_digit;               /* Seen any digits.  */
43   bool hex = false;             /* Look for hex float exponent.  */
44
45   /* The exponent of the number.  */
46   long int exponent;
47
48   if (nptr == NULL)
49     {
50       errno = EINVAL;
51       goto noconv;
52     }
53
54   s = nptr;
55
56   /* Eat whitespace.  */
57   while (isspace (*s))
58     ++s;
59
60   /* Get the sign.  */
61   negative = *s == '-';
62   if (*s == '-' || *s == '+')
63     ++s;
64
65   num = 0.0;
66   got_dot = false;
67   got_digit = false;
68   exponent = 0;
69
70   /* Check for hex float.  */
71   if (*s == '0' && c_tolower (s[1]) == 'x'
72       && (c_isxdigit (s[2]) || ('.' == s[2] && c_isxdigit (s[3]))))
73     {
74       hex = true;
75       s += 2;
76       for (;; ++s)
77         {
78           if (c_isxdigit (*s))
79             {
80               got_digit = true;
81
82               /* Make sure that multiplication by 16 will not overflow.  */
83               if (num > DBL_MAX / 16)
84                 /* The value of the digit doesn't matter, since we have already
85                    gotten as many digits as can be represented in a `double'.
86                    This doesn't necessarily mean the result will overflow.
87                    The exponent may reduce it to within range.
88
89                    We just need to record that there was another
90                    digit so that we can multiply by 16 later.  */
91                 ++exponent;
92               else
93                 num = ((num * 16.0)
94                        + (c_tolower (*s) - (c_isdigit (*s) ? '0' : 'a' - 10)));
95
96               /* Keep track of the number of digits after the decimal point.
97                  If we just divided by 16 here, we would lose precision.  */
98               if (got_dot)
99                 --exponent;
100             }
101           else if (!got_dot && *s == '.')
102             /* Record that we have found the decimal point.  */
103             got_dot = true;
104           else
105             /* Any other character terminates the number.  */
106             break;
107         }
108     }
109
110   /* Not a hex float.  */
111   else
112     {
113       for (;; ++s)
114         {
115           if (c_isdigit (*s))
116             {
117               got_digit = true;
118
119               /* Make sure that multiplication by 10 will not overflow.  */
120               if (num > DBL_MAX * 0.1)
121                 /* The value of the digit doesn't matter, since we have already
122                    gotten as many digits as can be represented in a `double'.
123                    This doesn't necessarily mean the result will overflow.
124                    The exponent may reduce it to within range.
125
126                    We just need to record that there was another
127                    digit so that we can multiply by 10 later.  */
128                 ++exponent;
129               else
130                 num = (num * 10.0) + (*s - '0');
131
132               /* Keep track of the number of digits after the decimal point.
133                  If we just divided by 10 here, we would lose precision.  */
134               if (got_dot)
135                 --exponent;
136             }
137           else if (!got_dot && *s == '.')
138             /* Record that we have found the decimal point.  */
139             got_dot = true;
140           else
141             /* Any other character terminates the number.  */
142             break;
143         }
144     }
145
146   if (!got_digit)
147     {
148       /* Check for infinities and NaNs.  */
149       if (c_tolower (*s) == 'i'
150           && c_tolower (s[1]) == 'n'
151           && c_tolower (s[2]) == 'f')
152         {
153           s += 3;
154           num = HUGE_VAL;
155           if (c_tolower (*s) == 'i'
156               && c_tolower (s[1]) == 'n'
157               && c_tolower (s[2]) == 'i'
158               && c_tolower (s[3]) == 't'
159               && c_tolower (s[4]) == 'y')
160             s += 5;
161           goto valid;
162         }
163 #ifdef NAN
164       else if (c_tolower (*s) == 'n'
165                && c_tolower (s[1]) == 'a'
166                && c_tolower (s[2]) == 'n')
167         {
168           s += 3;
169           num = NAN;
170           /* Since nan(<n-char-sequence>) is implementation-defined,
171              we define it by ignoring <n-char-sequence>.  A nicer
172              implementation would populate the bits of the NaN
173              according to interpreting n-char-sequence as a
174              hexadecimal number, but the result is still a NaN.  */
175           if (*s == '(')
176             {
177               const unsigned char *p = s + 1;
178               while (c_isalnum (*p))
179                 p++;
180               if (*p == ')')
181                 s = p + 1;
182             }
183           goto valid;
184         }
185 #endif
186       goto noconv;
187     }
188
189   if (c_tolower (*s) == (hex ? 'p' : 'e') && !isspace (s[1]))
190     {
191       /* Get the exponent specified after the `e' or `E'.  */
192       int save = errno;
193       char *end;
194       long int exp;
195
196       errno = 0;
197       ++s;
198       exp = strtol (s, &end, 10);
199       if (errno == ERANGE && num)
200         {
201           /* The exponent overflowed a `long int'.  It is probably a safe
202              assumption that an exponent that cannot be represented by
203              a `long int' exceeds the limits of a `double'.  */
204           if (endptr != NULL)
205             *endptr = end;
206           if (exp < 0)
207             goto underflow;
208           else
209             goto overflow;
210         }
211       else if (end == (char *) s)
212         /* There was no exponent.  Reset END to point to
213            the 'e' or 'E', so *ENDPTR will be set there.  */
214         end = (char *) s - 1;
215       errno = save;
216       s = end;
217       exponent += exp;
218     }
219
220   if (num == 0.0)
221     goto valid;
222
223   if (hex)
224     {
225       /* ldexp takes care of range errors.  */
226       num = ldexp (num, exponent);
227       goto valid;
228     }
229
230   /* Multiply NUM by 10 to the EXPONENT power,
231      checking for overflow and underflow.  */
232
233   if (exponent < 0)
234     {
235       if (num < DBL_MIN * pow (10.0, (double) -exponent))
236         goto underflow;
237     }
238   else if (exponent > 0)
239     {
240       if (num > DBL_MAX * pow (10.0, (double) -exponent))
241         goto overflow;
242     }
243
244   num *= pow (10.0, (double) exponent);
245
246  valid:
247   if (endptr != NULL)
248     *endptr = (char *) s;
249   return negative ? -num : num;
250
251  overflow:
252   /* Return an overflow error.  */
253   if (endptr != NULL)
254     *endptr = (char *) s;
255   errno = ERANGE;
256   return negative ? -HUGE_VAL : HUGE_VAL;
257
258  underflow:
259   /* Return an underflow error.  */
260   if (endptr != NULL)
261     *endptr = (char *) s;
262   errno = ERANGE;
263   return negative ? -0.0 : 0.0;
264
265  noconv:
266   /* There was no number.  */
267   if (endptr != NULL)
268     *endptr = (char *) nptr;
269   errno = EINVAL;
270   return 0.0;
271 }