Mercurial > gnulib
annotate lib/strtod.c @ 9821:18bab9955c43
Document various strtod bugs, with some fixes.
* doc/posix-functions/strtod.texi (strtod): Document bugs with
"-0x", "inf", "nan", and hex constants.
* doc/posix-functions/atof.texi (atof): Likewise.
* modules/stdlib (Makefile.am): Support strtod.
* m4/stdlib_h.m4 (gl_STDLIB_H_DEFAULTS): Likewise.
* m4/strtod.m4 (gl_FUNC_STRTOD): Fit in stdlib framework, and
detect additional strtod bugs.
* lib/stdlib.in.h (rpl_strtod): Add declarations.
* lib/strtod.c (strtod): Return -0.0 on negative underflow. Use
bool where appropriate. Parse 'inf' and 'nan'.
* tests/test-strtod.c: New file.
* modules/strtod (Depends-on): Add stdbool, stdlib.
(configure.ac): Turn on module indicator.
* modules/strtod-tests: New module.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Sat, 29 Mar 2008 13:50:21 -0600 |
parents | bbbbbf4cd1c5 |
children | fb687649f177 |
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 |
28 /* Convert NPTR to a double. If ENDPTR is not NULL, a pointer to the | |
29 character after the last one used in the number is put in *ENDPTR. */ | |
30 double | |
1691 | 31 strtod (const char *nptr, char **endptr) |
9 | 32 { |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
33 const char *s; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
34 bool negative = false; |
9 | 35 |
36 /* The number so far. */ | |
37 double num; | |
38 | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
39 bool got_dot; /* Found a decimal point. */ |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
40 bool got_digit; /* Seen any digits. */ |
9 | 41 |
42 /* The exponent of the number. */ | |
43 long int exponent; | |
44 | |
45 if (nptr == NULL) | |
46 { | |
47 errno = EINVAL; | |
48 goto noconv; | |
49 } | |
50 | |
51 s = nptr; | |
52 | |
53 /* Eat whitespace. */ | |
6927
fa896bb33133
* lib/memcasecmp.c: Include <limits.h>.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6834
diff
changeset
|
54 while (isspace ((unsigned char) *s)) |
9 | 55 ++s; |
56 | |
57 /* Get the sign. */ | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
58 negative = *s == '-'; |
9 | 59 if (*s == '-' || *s == '+') |
60 ++s; | |
61 | |
62 num = 0.0; | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
63 got_dot = false; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
64 got_digit = false; |
9 | 65 exponent = 0; |
66 for (;; ++s) | |
67 { | |
6927
fa896bb33133
* lib/memcasecmp.c: Include <limits.h>.
Paul Eggert <eggert@cs.ucla.edu>
parents:
6834
diff
changeset
|
68 if ('0' <= *s && *s <= '9') |
9 | 69 { |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
70 got_digit = true; |
9 | 71 |
72 /* Make sure that multiplication by 10 will not overflow. */ | |
73 if (num > DBL_MAX * 0.1) | |
74 /* The value of the digit doesn't matter, since we have already | |
75 gotten as many digits as can be represented in a `double'. | |
76 This doesn't necessarily mean the result will overflow. | |
77 The exponent may reduce it to within range. | |
78 | |
79 We just need to record that there was another | |
80 digit so that we can multiply by 10 later. */ | |
81 ++exponent; | |
82 else | |
83 num = (num * 10.0) + (*s - '0'); | |
84 | |
85 /* Keep track of the number of digits after the decimal point. | |
86 If we just divided by 10 here, we would lose precision. */ | |
87 if (got_dot) | |
88 --exponent; | |
89 } | |
311 | 90 else if (!got_dot && *s == '.') |
9 | 91 /* Record that we have found the decimal point. */ |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
92 got_dot = true; |
9 | 93 else |
94 /* Any other character terminates the number. */ | |
95 break; | |
96 } | |
97 | |
98 if (!got_digit) | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
99 { |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
100 /* Check for infinities and NaNs. */ |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
101 if (tolower ((unsigned char) *s) == 'i' |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
102 && tolower ((unsigned char) s[1]) == 'n' |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
103 && tolower ((unsigned char) s[2]) == 'f') |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
104 { |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
105 s += 3; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
106 num = HUGE_VAL; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
107 if (tolower ((unsigned char) *s) == 'i' |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
108 && tolower ((unsigned char) s[1]) == 'n' |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
109 && tolower ((unsigned char) s[2]) == 'i' |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
110 && tolower ((unsigned char) s[3]) == 't' |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
111 && tolower ((unsigned char) s[4]) == 'y') |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
112 s += 5; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
113 goto valid; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
114 } |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
115 #ifdef NAN |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
116 else if (tolower ((unsigned char) *s) == 'n' |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
117 && tolower ((unsigned char) s[1]) == 'a' |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
118 && tolower ((unsigned char) s[2]) == 'n') |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
119 { |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
120 s += 3; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
121 num = NAN; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
122 /* 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
|
123 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
|
124 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
|
125 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
|
126 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
|
127 if (*s == '(') |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
128 { |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
129 const char *p = s + 1; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
130 while (isalnum ((unsigned char) *p)) |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
131 p++; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
132 if (*p == ')') |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
133 s = p + 1; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
134 } |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
135 goto valid; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
136 } |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
137 #endif |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
138 goto noconv; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
139 } |
9 | 140 |
6930
188d89808804
* strtod.c (strtod): cast the argument of tolower to unsigned char.
Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
parents:
6927
diff
changeset
|
141 if (tolower ((unsigned char) *s) == 'e') |
9 | 142 { |
143 /* Get the exponent specified after the `e' or `E'. */ | |
144 int save = errno; | |
145 char *end; | |
146 long int exp; | |
147 | |
148 errno = 0; | |
149 ++s; | |
311 | 150 exp = strtol (s, &end, 10); |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
151 if (errno == ERANGE && num) |
9 | 152 { |
153 /* The exponent overflowed a `long int'. It is probably a safe | |
154 assumption that an exponent that cannot be represented by | |
155 a `long int' exceeds the limits of a `double'. */ | |
156 if (endptr != NULL) | |
157 *endptr = end; | |
158 if (exp < 0) | |
159 goto underflow; | |
160 else | |
161 goto overflow; | |
162 } | |
163 else if (end == s) | |
164 /* There was no exponent. Reset END to point to | |
165 the 'e' or 'E', so *ENDPTR will be set there. */ | |
166 end = (char *) s - 1; | |
167 errno = save; | |
168 s = end; | |
169 exponent += exp; | |
170 } | |
171 | |
172 if (num == 0.0) | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
173 goto valid; |
9 | 174 |
175 /* Multiply NUM by 10 to the EXPONENT power, | |
176 checking for overflow and underflow. */ | |
177 | |
178 if (exponent < 0) | |
179 { | |
311 | 180 if (num < DBL_MIN * pow (10.0, (double) -exponent)) |
9 | 181 goto underflow; |
182 } | |
183 else if (exponent > 0) | |
184 { | |
311 | 185 if (num > DBL_MAX * pow (10.0, (double) -exponent)) |
9 | 186 goto overflow; |
187 } | |
188 | |
311 | 189 num *= pow (10.0, (double) exponent); |
9 | 190 |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
191 valid: |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
192 if (endptr != NULL) |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
193 *endptr = (char *) s; |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
194 return negative ? -num : num; |
9 | 195 |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
196 overflow: |
9 | 197 /* Return an overflow error. */ |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
198 if (endptr != NULL) |
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
199 *endptr = (char *) s; |
9 | 200 errno = ERANGE; |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
201 return negative ? -HUGE_VAL : HUGE_VAL; |
9 | 202 |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
203 underflow: |
9 | 204 /* Return an underflow error. */ |
205 if (endptr != NULL) | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
206 *endptr = (char *) s; |
9 | 207 errno = ERANGE; |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
208 return negative ? -0.0 : 0.0; |
9 | 209 |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
210 noconv: |
9 | 211 /* There was no number. */ |
212 if (endptr != NULL) | |
213 *endptr = (char *) nptr; | |
9821
18bab9955c43
Document various strtod bugs, with some fixes.
Eric Blake <ebb9@byu.net>
parents:
9309
diff
changeset
|
214 errno = EINVAL; |
9 | 215 return 0.0; |
216 } |