Mercurial > gnulib
annotate lib/strtod.c @ 9822:fb687649f177
Add hex float support.
* modules/strtod (Depends-on): Add c-ctype.
(Link): Mention POW_LIB.
* lib/strtod.c (strtod): Recognize hex floats. Don't allow
whitespace between 'e' and exponent.
* tests/test-strtod.c (main): Enable hex float tests.
* doc/posix-functions/strtod.texi (strtod): Document what gnulib
now provides.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Sat, 29 Mar 2008 21:24:07 -0600 |
parents | 18bab9955c43 |
children | 48eddbd8edd5 |
rev | line source |
---|---|
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
1 /* Copyright (C) 1991, 1992, 1997, 1999, 2003, 2006, 2008 Free |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
2 Software Foundation, Inc. |
9 | 3 |
9309
bbbbbf4cd1c5
Change copyright notice from GPLv2+ to GPLv3+.
Bruno Haible <bruno@clisp.org>
parents:
7302
diff
changeset
|
4 This program is free software: you can redistribute it and/or modify |
311 | 5 it under the terms of the GNU General Public License as published by |
9309
bbbbbf4cd1c5
Change copyright notice from GPLv2+ to GPLv3+.
Bruno Haible <bruno@clisp.org>
parents:
7302
diff
changeset
|
6 the Free Software Foundation; either version 3 of the License, or |
bbbbbf4cd1c5
Change copyright notice from GPLv2+ to GPLv3+.
Bruno Haible <bruno@clisp.org>
parents:
7302
diff
changeset
|
7 (at your option) any later version. |
9 | 8 |
311 | 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 | |
9309
bbbbbf4cd1c5
Change copyright notice from GPLv2+ to GPLv3+.
Bruno Haible <bruno@clisp.org>
parents:
7302
diff
changeset
|
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
309 | 16 |
7302
8a1a9361108c
* _fpending.c: Include <config.h> unconditionally, since we no
Paul Eggert <eggert@cs.ucla.edu>
parents:
6930
diff
changeset
|
17 #include <config.h> |
311 | 18 |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
19 #include <stdlib.h> |
373 | 20 |
311 | 21 #include <ctype.h> |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
22 #include <errno.h> |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
23 #include <float.h> |
311 | 24 #include <math.h> |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
25 #include <stdbool.h> |
4691 | 26 #include <string.h> |
9 | 27 |
9822 | 28 #include "c-ctype.h" |
29 | |
9 | 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 | |
1691 | 33 strtod (const char *nptr, char **endptr) |
9 | 34 { |
9822 | 35 const unsigned char *s; |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
36 bool negative = false; |
9 | 37 |
38 /* The number so far. */ | |
39 double num; | |
40 | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
41 bool got_dot; /* Found a decimal point. */ |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
42 bool got_digit; /* Seen any digits. */ |
9822 | 43 bool hex = false; /* Look for hex float exponent. */ |
9 | 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. */ | |
9822 | 57 while (isspace (*s)) |
9 | 58 ++s; |
59 | |
60 /* Get the sign. */ | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
61 negative = *s == '-'; |
9 | 62 if (*s == '-' || *s == '+') |
63 ++s; | |
64 | |
65 num = 0.0; | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
66 got_dot = false; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
67 got_digit = false; |
9 | 68 exponent = 0; |
9822 | 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])))) | |
9 | 73 { |
9822 | 74 hex = true; |
75 s += 2; | |
76 for (;; ++s) | |
9 | 77 { |
9822 | 78 if (c_isxdigit (*s)) |
79 { | |
80 got_digit = true; | |
9 | 81 |
9822 | 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))); | |
9 | 95 |
9822 | 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; | |
9 | 104 else |
9822 | 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; | |
9 | 118 |
9822 | 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; | |
9 | 143 } |
144 } | |
145 | |
146 if (!got_digit) | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
147 { |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
148 /* Check for infinities and NaNs. */ |
9822 | 149 if (c_tolower (*s) == 'i' |
150 && c_tolower (s[1]) == 'n' | |
151 && c_tolower (s[2]) == 'f') | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
152 { |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
153 s += 3; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
154 num = HUGE_VAL; |
9822 | 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') | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
160 s += 5; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
161 goto valid; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
162 } |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
163 #ifdef NAN |
9822 | 164 else if (c_tolower (*s) == 'n' |
165 && c_tolower (s[1]) == 'a' | |
166 && c_tolower (s[2]) == 'n') | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
167 { |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
168 s += 3; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
169 num = NAN; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
170 /* Since nan(<n-char-sequence>) is implementation-defined, |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
171 we define it by ignoring <n-char-sequence>. A nicer |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
172 implementation would populate the bits of the NaN |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
173 according to interpreting n-char-sequence as a |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
174 hexadecimal number, but the result is still a NaN. */ |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
175 if (*s == '(') |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
176 { |
9822 | 177 const unsigned char *p = s + 1; |
178 while (c_isalnum (*p)) | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
179 p++; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
180 if (*p == ')') |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
181 s = p + 1; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
182 } |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
183 goto valid; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
184 } |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
185 #endif |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
186 goto noconv; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
187 } |
9 | 188 |
9822 | 189 if (c_tolower (*s) == (hex ? 'p' : 'e') && !isspace (s[1])) |
9 | 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; | |
311 | 198 exp = strtol (s, &end, 10); |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
199 if (errno == ERANGE && num) |
9 | 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 } | |
9822 | 211 else if (end == (char *) s) |
9 | 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) | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
221 goto valid; |
9 | 222 |
9822 | 223 if (hex) |
224 { | |
225 /* ldexp takes care of range errors. */ | |
226 num = ldexp (num, exponent); | |
227 goto valid; | |
228 } | |
229 | |
9 | 230 /* Multiply NUM by 10 to the EXPONENT power, |
231 checking for overflow and underflow. */ | |
232 | |
233 if (exponent < 0) | |
234 { | |
311 | 235 if (num < DBL_MIN * pow (10.0, (double) -exponent)) |
9 | 236 goto underflow; |
237 } | |
238 else if (exponent > 0) | |
239 { | |
311 | 240 if (num > DBL_MAX * pow (10.0, (double) -exponent)) |
9 | 241 goto overflow; |
242 } | |
243 | |
311 | 244 num *= pow (10.0, (double) exponent); |
9 | 245 |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
246 valid: |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
247 if (endptr != NULL) |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
248 *endptr = (char *) s; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
249 return negative ? -num : num; |
9 | 250 |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
251 overflow: |
9 | 252 /* Return an overflow error. */ |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
253 if (endptr != NULL) |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
254 *endptr = (char *) s; |
9 | 255 errno = ERANGE; |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
256 return negative ? -HUGE_VAL : HUGE_VAL; |
9 | 257 |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
258 underflow: |
9 | 259 /* Return an underflow error. */ |
260 if (endptr != NULL) | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
261 *endptr = (char *) s; |
9 | 262 errno = ERANGE; |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
263 return negative ? -0.0 : 0.0; |
9 | 264 |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
265 noconv: |
9 | 266 /* There was no number. */ |
267 if (endptr != NULL) | |
268 *endptr = (char *) nptr; | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
269 errno = EINVAL; |
9 | 270 return 0.0; |
271 } |