OSDN Git Service

Fix include/errno.h to not use kernel header, and instead use bits/errno.h.
[uclinux-h8/uClibc.git] / libc / stdlib / strtod.c
1 /*
2  * Copyright (C) 2000 Manuel Novoa III
3  *
4  * Notes:
5  *
6  * The primary objective of this implementation was minimal size while
7  * providing robustness and resonable accuracy.
8  *
9  * This implementation depends on IEEE floating point behavior and expects
10  * to be able to generate +/- infinity as a result.
11  *
12  * There are a number of compile-time options below.
13  *
14  */
15
16 /*****************************************************************************/
17 /*                            OPTIONS                                        */
18 /*****************************************************************************/
19
20 /* Set if we want to scale with a O(log2(exp)) multiplications. */
21 #define _STRTOD_LOG_SCALING      1
22
23 /* Set if we want strtod to set errno appropriately. */
24 /* NOTE: Implies all options below and pulls in _zero_or_inf_check. */
25 #define _STRTOD_ERRNO            0
26
27 /* Set if we want support for the endptr arg. */
28 /* Implied by _STRTOD_ERRNO. */
29 #define _STRTOD_ENDPTR           1
30
31 /* Set if we want to prevent overflow in accumulating the exponent. */
32 #define _STRTOD_RESTRICT_EXP     1
33
34 /* Set if we want to process mantissa digits more intelligently. */
35 /* Implied by _STRTOD_ERRNO. */
36 #define _STRTOD_RESTRICT_DIGITS  1
37
38 /* Set if we want to skip scaling 0 for the exponent. */
39 /* Implied by _STRTOD_ERRNO. */
40 #define _STRTOD_ZERO_CHECK       0
41
42 /*****************************************************************************/
43 /* Don't change anything that follows.                                       */
44 /*****************************************************************************/
45
46 #if _STRTOD_ERRNO
47 #undef _STRTOD_ENDPTR
48 #undef _STRTOD_RESTRICT_EXP
49 #undef _STRTOD_RESTRICT_DIGITS
50 #undef _STRTOD_ZERO_CHECK
51 #define _STRTOD_ENDPTR           1
52 #define _STRTOD_RESTRICT_EXP     1
53 #define _STRTOD_RESTRICT_DIGITS  1
54 #define _STRTOD_ZERO_CHECK       1
55 #endif
56
57 /*****************************************************************************/
58
59 #include <stdlib.h>
60
61 #include <float.h>
62
63 #if _STRTOD_RESTRICT_DIGITS
64 #define MAX_SIG_DIGITS 20
65 #define EXP_DENORM_ADJUST MAX_SIG_DIGITS
66 #define MAX_ALLOWED_EXP (MAX_SIG_DIGITS  + EXP_DENORM_ADJUST - DBL_MIN_10_EXP)
67
68 #if DBL_DIG > MAX_SIG_DIGITS
69 #error need to adjust MAX_SIG_DIGITS
70 #endif
71
72 #include <limits.h>
73 #if MAX_ALLOWED_EXP > INT_MAX
74 #error size assumption violated for MAX_ALLOWED_EXP
75 #endif
76 #else
77 /* We want some excess if we're not restricting mantissa digits. */
78 #define MAX_ALLOWED_EXP ((20 - DBL_MIN_10_EXP) * 2)
79 #endif
80
81 #include <ctype.h>
82 /* Note: For i386 the macro resulted in smaller code than the function call. */
83 #if 1
84 #undef isdigit
85 #define isdigit(x) ( (x >= '0') && (x <= '9') )
86 #endif
87
88 #if _STRTOD_ERRNO
89 #include <errno.h>
90 extern int _zero_or_inf_check(double x);
91 #endif
92
93 double strtod(const char *str, char **endptr)
94 {
95     double number;
96 #if _STRTOD_LOG_SCALING
97     double p10;
98 #endif
99     char *pos0;
100 #if _STRTOD_ENDPTR
101     char *pos1;
102 #endif
103     char *pos = (char *) str;
104     int exponent_power;
105     int exponent_temp;
106     int negative;
107 #if _STRTOD_RESTRICT_DIGITS || _STRTOD_ENDPTR
108     int num_digits;
109 #endif
110
111     while (isspace(*pos)) {     /* skip leading whitespace */
112         ++pos;
113     }
114
115     negative = 0;
116     switch(*pos) {              /* handle optional sign */
117     case '-': negative = 1;     /* fall through to increment position */
118     case '+': ++pos;
119     }
120
121     number = 0.;
122 #if _STRTOD_RESTRICT_DIGITS || _STRTOD_ENDPTR
123     num_digits = -1;
124 #endif
125     exponent_power = 0;
126     pos0 = NULL;
127
128  LOOP:
129     while (isdigit(*pos)) {     /* process string of digits */
130 #if _STRTOD_RESTRICT_DIGITS
131         if (num_digits < 0) {   /* first time through? */
132             ++num_digits;       /* we've now seen a digit */
133         }
134         if (num_digits || (*pos != '0')) { /* had/have nonzero */
135             ++num_digits;
136             if (num_digits <= MAX_SIG_DIGITS) { /* is digit significant */
137                 number = number * 10. + (*pos - '0');
138             }
139         }
140 #else
141 #if _STRTOD_ENDPTR
142         ++num_digits;
143 #endif
144         number = number * 10. + (*pos - '0');
145 #endif
146         ++pos;
147     }
148
149     if ((*pos == '.') && !pos0) { /* is this the first decimal point? */
150         pos0 = ++pos;           /* save position of decimal point */
151         goto LOOP;              /* and process rest of digits */
152     }
153
154 #if _STRTOD_ENDPTR
155     if (num_digits<0) {         /* must have at least one digit */
156         pos = (char *) str;
157         goto DONE;
158     }
159 #endif
160
161 #if _STRTOD_RESTRICT_DIGITS
162     if (num_digits > MAX_SIG_DIGITS) { /* adjust exponent for skipped digits */
163         exponent_power += num_digits - MAX_SIG_DIGITS;
164     }
165 #endif
166
167     if (pos0) {
168         exponent_power += pos0 - pos; /* adjust exponent for decimal point */
169     }
170
171     if (negative) {             /* correct for sign */
172         number = -number;
173         negative = 0;           /* reset for exponent processing below */
174     }
175
176     /* process an exponent string */
177     if (*pos == 'e' || *pos == 'E') {
178 #if _STRTOD_ENDPTR
179         pos1 = pos;
180 #endif
181         switch(*++pos) {        /* handle optional sign */
182         case '-': negative = 1; /* fall through to increment pos */
183         case '+': ++pos;
184         }
185
186         pos0 = pos;
187         exponent_temp = 0;
188         while (isdigit(*pos)) { /* process string of digits */
189 #if _STRTOD_RESTRICT_EXP
190             if (exponent_temp < MAX_ALLOWED_EXP) { /* overflow check */
191                 exponent_temp = exponent_temp * 10 + (*pos - '0');
192             }
193 #else
194             exponent_temp = exponent_temp * 10 + (*pos - '0');
195 #endif
196             ++pos;
197         }
198
199 #if _STRTOD_ENDPTR
200         if (pos == pos0) {      /* were there no digits? */
201             pos = pos1;         /* back up to e|E */
202         } /* else */
203 #endif
204         if (negative) {
205             exponent_power -= exponent_temp;
206         } else {
207             exponent_power += exponent_temp;
208         }
209     }
210
211 #if _STRTOD_ZERO_CHECK
212     if (number == 0.) {
213         goto DONE;
214     }
215 #endif
216
217     /* scale the result */
218 #if _STRTOD_LOG_SCALING
219     exponent_temp = exponent_power;
220     p10 = 10.;
221
222     if (exponent_temp < 0) {
223         exponent_temp = -exponent_temp;
224     }
225
226     while (exponent_temp) {
227         if (exponent_temp & 1) {
228             if (exponent_power < 0) {
229                 number /= p10;
230             } else {
231                 number *= p10;
232             }
233         }
234         exponent_temp >>= 1;
235         p10 *= p10;
236     }
237 #else
238     while (exponent_power) {
239         if (exponent_power < 0) {
240             number /= 10.;
241             exponent_power++;
242         } else {
243             number *= 10.;
244             exponent_power--;
245         }
246     }
247 #endif
248
249 #if _STRTOD_ERRNO
250     if (_zero_or_inf_check(number)) {
251         __set_errno(ERANGE);
252     }
253 #endif
254
255  DONE:
256 #if _STRTOD_ENDPTR
257     if (endptr) {
258         *endptr = pos;
259     }
260 #endif
261
262     return number;
263 }