Mercurial > gnulib
comparison 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 |
comparison
equal
deleted
inserted
replaced
9821:18bab9955c43 | 9822:fb687649f177 |
---|---|
23 #include <float.h> | 23 #include <float.h> |
24 #include <math.h> | 24 #include <math.h> |
25 #include <stdbool.h> | 25 #include <stdbool.h> |
26 #include <string.h> | 26 #include <string.h> |
27 | 27 |
28 #include "c-ctype.h" | |
29 | |
28 /* Convert NPTR to a double. If ENDPTR is not NULL, a pointer to the | 30 /* 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. */ | 31 character after the last one used in the number is put in *ENDPTR. */ |
30 double | 32 double |
31 strtod (const char *nptr, char **endptr) | 33 strtod (const char *nptr, char **endptr) |
32 { | 34 { |
33 const char *s; | 35 const unsigned char *s; |
34 bool negative = false; | 36 bool negative = false; |
35 | 37 |
36 /* The number so far. */ | 38 /* The number so far. */ |
37 double num; | 39 double num; |
38 | 40 |
39 bool got_dot; /* Found a decimal point. */ | 41 bool got_dot; /* Found a decimal point. */ |
40 bool got_digit; /* Seen any digits. */ | 42 bool got_digit; /* Seen any digits. */ |
43 bool hex = false; /* Look for hex float exponent. */ | |
41 | 44 |
42 /* The exponent of the number. */ | 45 /* The exponent of the number. */ |
43 long int exponent; | 46 long int exponent; |
44 | 47 |
45 if (nptr == NULL) | 48 if (nptr == NULL) |
49 } | 52 } |
50 | 53 |
51 s = nptr; | 54 s = nptr; |
52 | 55 |
53 /* Eat whitespace. */ | 56 /* Eat whitespace. */ |
54 while (isspace ((unsigned char) *s)) | 57 while (isspace (*s)) |
55 ++s; | 58 ++s; |
56 | 59 |
57 /* Get the sign. */ | 60 /* Get the sign. */ |
58 negative = *s == '-'; | 61 negative = *s == '-'; |
59 if (*s == '-' || *s == '+') | 62 if (*s == '-' || *s == '+') |
61 | 64 |
62 num = 0.0; | 65 num = 0.0; |
63 got_dot = false; | 66 got_dot = false; |
64 got_digit = false; | 67 got_digit = false; |
65 exponent = 0; | 68 exponent = 0; |
66 for (;; ++s) | 69 |
67 { | 70 /* Check for hex float. */ |
68 if ('0' <= *s && *s <= '9') | 71 if (*s == '0' && c_tolower (s[1]) == 'x' |
69 { | 72 && (c_isxdigit (s[2]) || ('.' == s[2] && c_isxdigit (s[3])))) |
70 got_digit = true; | 73 { |
71 | 74 hex = true; |
72 /* Make sure that multiplication by 10 will not overflow. */ | 75 s += 2; |
73 if (num > DBL_MAX * 0.1) | 76 for (;; ++s) |
74 /* The value of the digit doesn't matter, since we have already | 77 { |
75 gotten as many digits as can be represented in a `double'. | 78 if (c_isxdigit (*s)) |
76 This doesn't necessarily mean the result will overflow. | 79 { |
77 The exponent may reduce it to within range. | 80 got_digit = true; |
78 | 81 |
79 We just need to record that there was another | 82 /* Make sure that multiplication by 16 will not overflow. */ |
80 digit so that we can multiply by 10 later. */ | 83 if (num > DBL_MAX / 16) |
81 ++exponent; | 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; | |
82 else | 104 else |
83 num = (num * 10.0) + (*s - '0'); | 105 /* Any other character terminates the number. */ |
84 | 106 break; |
85 /* Keep track of the number of digits after the decimal point. | 107 } |
86 If we just divided by 10 here, we would lose precision. */ | 108 } |
87 if (got_dot) | 109 |
88 --exponent; | 110 /* Not a hex float. */ |
89 } | 111 else |
90 else if (!got_dot && *s == '.') | 112 { |
91 /* Record that we have found the decimal point. */ | 113 for (;; ++s) |
92 got_dot = true; | 114 { |
93 else | 115 if (c_isdigit (*s)) |
94 /* Any other character terminates the number. */ | 116 { |
95 break; | 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 } | |
96 } | 144 } |
97 | 145 |
98 if (!got_digit) | 146 if (!got_digit) |
99 { | 147 { |
100 /* Check for infinities and NaNs. */ | 148 /* Check for infinities and NaNs. */ |
101 if (tolower ((unsigned char) *s) == 'i' | 149 if (c_tolower (*s) == 'i' |
102 && tolower ((unsigned char) s[1]) == 'n' | 150 && c_tolower (s[1]) == 'n' |
103 && tolower ((unsigned char) s[2]) == 'f') | 151 && c_tolower (s[2]) == 'f') |
104 { | 152 { |
105 s += 3; | 153 s += 3; |
106 num = HUGE_VAL; | 154 num = HUGE_VAL; |
107 if (tolower ((unsigned char) *s) == 'i' | 155 if (c_tolower (*s) == 'i' |
108 && tolower ((unsigned char) s[1]) == 'n' | 156 && c_tolower (s[1]) == 'n' |
109 && tolower ((unsigned char) s[2]) == 'i' | 157 && c_tolower (s[2]) == 'i' |
110 && tolower ((unsigned char) s[3]) == 't' | 158 && c_tolower (s[3]) == 't' |
111 && tolower ((unsigned char) s[4]) == 'y') | 159 && c_tolower (s[4]) == 'y') |
112 s += 5; | 160 s += 5; |
113 goto valid; | 161 goto valid; |
114 } | 162 } |
115 #ifdef NAN | 163 #ifdef NAN |
116 else if (tolower ((unsigned char) *s) == 'n' | 164 else if (c_tolower (*s) == 'n' |
117 && tolower ((unsigned char) s[1]) == 'a' | 165 && c_tolower (s[1]) == 'a' |
118 && tolower ((unsigned char) s[2]) == 'n') | 166 && c_tolower (s[2]) == 'n') |
119 { | 167 { |
120 s += 3; | 168 s += 3; |
121 num = NAN; | 169 num = NAN; |
122 /* Since nan(<n-char-sequence>) is implementation-defined, | 170 /* Since nan(<n-char-sequence>) is implementation-defined, |
123 we define it by ignoring <n-char-sequence>. A nicer | 171 we define it by ignoring <n-char-sequence>. A nicer |
124 implementation would populate the bits of the NaN | 172 implementation would populate the bits of the NaN |
125 according to interpreting n-char-sequence as a | 173 according to interpreting n-char-sequence as a |
126 hexadecimal number, but the result is still a NaN. */ | 174 hexadecimal number, but the result is still a NaN. */ |
127 if (*s == '(') | 175 if (*s == '(') |
128 { | 176 { |
129 const char *p = s + 1; | 177 const unsigned char *p = s + 1; |
130 while (isalnum ((unsigned char) *p)) | 178 while (c_isalnum (*p)) |
131 p++; | 179 p++; |
132 if (*p == ')') | 180 if (*p == ')') |
133 s = p + 1; | 181 s = p + 1; |
134 } | 182 } |
135 goto valid; | 183 goto valid; |
136 } | 184 } |
137 #endif | 185 #endif |
138 goto noconv; | 186 goto noconv; |
139 } | 187 } |
140 | 188 |
141 if (tolower ((unsigned char) *s) == 'e') | 189 if (c_tolower (*s) == (hex ? 'p' : 'e') && !isspace (s[1])) |
142 { | 190 { |
143 /* Get the exponent specified after the `e' or `E'. */ | 191 /* Get the exponent specified after the `e' or `E'. */ |
144 int save = errno; | 192 int save = errno; |
145 char *end; | 193 char *end; |
146 long int exp; | 194 long int exp; |
158 if (exp < 0) | 206 if (exp < 0) |
159 goto underflow; | 207 goto underflow; |
160 else | 208 else |
161 goto overflow; | 209 goto overflow; |
162 } | 210 } |
163 else if (end == s) | 211 else if (end == (char *) s) |
164 /* There was no exponent. Reset END to point to | 212 /* There was no exponent. Reset END to point to |
165 the 'e' or 'E', so *ENDPTR will be set there. */ | 213 the 'e' or 'E', so *ENDPTR will be set there. */ |
166 end = (char *) s - 1; | 214 end = (char *) s - 1; |
167 errno = save; | 215 errno = save; |
168 s = end; | 216 s = end; |
170 } | 218 } |
171 | 219 |
172 if (num == 0.0) | 220 if (num == 0.0) |
173 goto valid; | 221 goto valid; |
174 | 222 |
223 if (hex) | |
224 { | |
225 /* ldexp takes care of range errors. */ | |
226 num = ldexp (num, exponent); | |
227 goto valid; | |
228 } | |
229 | |
175 /* Multiply NUM by 10 to the EXPONENT power, | 230 /* Multiply NUM by 10 to the EXPONENT power, |
176 checking for overflow and underflow. */ | 231 checking for overflow and underflow. */ |
177 | 232 |
178 if (exponent < 0) | 233 if (exponent < 0) |
179 { | 234 { |