unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / language / c / libc / stdlib / v2_0 / src / strtod.cxx
1 //===========================================================================
2 //
3 //      strtod.cxx
4 //
5 //      ISO String to double-precision floating point conversion
6 //
7 //===========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //===========================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):    jlarmour
44 // Contributors: 
45 // Date:         2000-04-30
46 // Purpose:     
47 // Description: 
48 // Usage:       
49 //
50 //####DESCRIPTIONEND####
51 //
52 //===========================================================================
53
54 // CONFIGURATION
55
56 #include <pkgconf/libc_stdlib.h>           // Configuration header
57
58 // Include strtod()?
59 #if defined(CYGFUN_LIBC_strtod)
60
61 // INCLUDES
62
63 #include <cyg/infra/cyg_type.h>     // Common type definitions and support
64 #include <cyg/infra/cyg_trac.h>     // Tracing support
65 #include <cyg/infra/cyg_ass.h>      // Assertion support
66 #include <stddef.h>                 // NULL, wchar_t and size_t from compiler
67 #include <stdlib.h>                 // Main header for stdlib functions
68 #include <ctype.h>                  // isspace() and isdigit()
69 #include <float.h>                  // DBL_MIN_10_EXP and DBL_MAX_10_EXP
70 #include <math.h>                   // HUGE_VAL
71 #include <errno.h>                  // errno
72
73 // CONSTANTS
74
75 #define MAXE (DBL_MAX_10_EXP)
76 #define MINE (DBL_MIN_10_EXP)
77
78 // flags
79 #define SIGN    0x01
80 #define ESIGN   0x02
81 #define DECP    0x04
82
83
84 // MACROS
85
86 #define Ise(c)          ((c == 'e') || (c == 'E'))
87 #define Issign(c)       ((c == '-') || (c == '+'))
88 #define Val(c)          ((c - '0'))
89
90
91 // FUNCTIONS
92
93 /*
94  * [atw] multiply 64 bit accumulator by 10 and add digit.
95  * The KA/CA way to do this should be to use
96  * a 64-bit integer internally and use "adjust" to
97  * convert it to float at the end of processing.
98  */
99 static int
100 ten_mul(double *acc, int digit)
101 {
102     /* [atw] Crude, but effective (at least on a KB)...
103      */
104     *acc *= 10;
105     *acc += digit;
106     
107     return 0;     /* no overflow */
108 } // ten_mul()
109
110
111 /*
112  * compute 10**x by successive squaring.
113  */
114
115 static const double
116 exp10(unsigned x)
117 {
118     static double powtab[] = {1.0,
119                               10.0,
120                               100.0,
121                               1000.0,
122                               10000.0};
123     
124     if (x < (sizeof(powtab)/sizeof(double)))
125         return powtab[x];
126     else if (x & 1)
127         return 10.0 * exp10(x-1);
128     else
129         return exp10(x/2) * exp10(x/2);
130 } // exp10()
131
132
133 /*
134  * return (*acc) scaled by 10**dexp.
135  */
136
137 static double
138 adjust(double *acc, int dexp, int sign)
139      /* *acc    the 64 bit accumulator */
140      /* dexp    decimal exponent       */
141      /* sign    sign flag              */
142 {
143     double r;
144     
145     if (dexp > MAXE)
146     {
147         errno = ERANGE;
148         return (sign) ? -HUGE_VAL : HUGE_VAL;
149     }
150     else if (dexp < MINE)
151     {
152         errno = ERANGE;
153         return 0.0;
154     }
155     
156     r = *acc;
157     if (sign)
158         r = -r;
159     if (dexp==0)
160         return r;
161     
162     if (dexp < 0)
163         return r / exp10(abs(dexp));
164     else
165         return r * exp10(dexp);
166 } // adjust()
167
168
169 externC double
170 strtod( const char *nptr, char **endptr )
171 {
172     const char *start=nptr;
173     double accum = 0.0;
174     int flags = 0;
175     int texp  = 0;
176     int e     = 0;
177     int conv_done = 0;
178   
179     double retval;
180
181     CYG_REPORT_FUNCNAMETYPE( "strtod", "returning %f" );
182
183     CYG_CHECK_DATA_PTR( nptr, "nptr is an invalid pointer!" );
184
185     // endptr is allowed to be NULL, but if it isn't, we check it
186     if (endptr != NULL)
187         CYG_CHECK_DATA_PTR( endptr, "endptr is an invalid pointer!" );
188     
189     while(isspace(*nptr)) nptr++;
190     if(*nptr == '\0')
191     {   /* just leading spaces */
192         if(endptr != NULL) *endptr = (char *)start;
193         return 0.0;
194     }
195     
196     
197     if(Issign(*nptr))
198     {
199         if(*nptr == '-') flags = SIGN;
200         if(*++nptr == '\0')
201         {   /* "+|-" : should be an error ? */
202             if(endptr != NULL) *endptr = (char *)start;
203             return 0.0;
204         }
205     }
206     
207     for(; (isdigit(*nptr) || (*nptr == '.')); nptr++)
208     {
209         conv_done = 1;
210         if(*nptr == '.')
211             flags |= DECP;
212         else
213         {
214             if( ten_mul(&accum, Val(*nptr)) ) texp++;
215             if(flags & DECP) texp--;
216         }
217     }
218     
219     if(Ise(*nptr))
220     {
221         conv_done = 1;
222         if(*++nptr != '\0') /* skip e|E */
223         {  /* ! ([nptr]xxx[.[yyy]]e)  */
224             
225             while(isspace(*nptr)) nptr++; /* Ansi allows spaces after e */
226             if(*nptr != '\0')
227             { /*  ! ([nptr]xxx[.[yyy]]e[space])  */
228                 
229                 if(Issign(*nptr))
230                     if(*nptr++ == '-') flags |= ESIGN;
231                 
232                 if(*nptr != '\0')
233                 { /*  ! ([nptr]xxx[.[yyy]]e[nptr])  -- error?? */
234                     
235                     for(; isdigit(*nptr); nptr++)
236                         if (e < MAXE) /* prevent from grossly overflowing */
237                             e = e*10 + Val(*nptr);
238                     
239                     /* dont care what comes after this */
240                     if(flags & ESIGN)
241                         texp -= e;
242                     else
243                         texp += e;
244                 }
245             }
246         }
247     }
248     
249     if(endptr != NULL) 
250         *endptr = (char *)((conv_done) ? nptr : start);
251     
252     retval = adjust(&accum, (int)texp, (int)(flags & SIGN));
253   
254
255     CYG_REPORT_RETVAL( retval );
256
257     return retval;
258 } // strtod()
259
260
261 #endif // if defined(CYGFUN_LIBC_strtod)
262
263 // EOF strtod.cxx