1 /******************************************************************************
2  * $Id: cpl_strtod.cpp 15802 2008-11-23 20:11:36Z dron $
3  *
4  * Project:  CPL - Common Portability Library
5  * Purpose:  Functions to convert ASCII string to floating point number.
6  * Author:   Andrey Kiselev, dron@ak4719.spb.edu.
7  *
8  ******************************************************************************
9  * Copyright (c) 2006, Andrey Kiselev
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include <locale.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 
34 #include "cpl_conv.h"
35 
36 CPL_CVSID("$Id: cpl_strtod.cpp 15802 2008-11-23 20:11:36Z dron $");
37 
38 // XXX: with GCC 2.95 strtof() function is only available when in c99 mode.
39 // Fix it here not touching the compiler options.
40 #if defined(HAVE_STRTOF) && !HAVE_DECL_STRTOF
41 extern "C" {
42 extern float strtof(const char *nptr, char **endptr);
43 }
44 #endif
45 
46 /************************************************************************/
47 /*                            CPLAtofDelim()                            */
48 /************************************************************************/
49 
50 /**
51  * Converts ASCII string to floating point number.
52  *
53  * This function converts the initial portion of the string pointed to
54  * by nptr to double floating point representation. The behaviour is the
55  * same as
56  *
57  *   CPLStrtodDelim(nptr, (char **)NULL, point);
58  *
59  * This function does the same as standard atof(3), but does not take locale
60  * in account. Instead of locale defined decimal delimiter you can specify
61  * your own one. Also see notes for CPLAtof() function.
62  *
63  * @param nptr Pointer to string to convert.
64  * @param point Decimal delimiter.
65  *
66  * @return Converted value, if any.
67  */
CPLAtofDelim(const char * nptr,char point)68 double CPLAtofDelim(const char *nptr, char point)
69 {
70   return CPLStrtodDelim(nptr, 0, point);
71 }
72 
73 /************************************************************************/
74 /*                              CPLAtof()                               */
75 /************************************************************************/
76 
77 /**
78  * Converts ASCII string to floating point number.
79  *
80  * This function converts the initial portion of the string pointed to
81  * by nptr to double floating point representation. The behaviour is the
82  * same as
83  *
84  *   CPLStrtod(nptr, (char **)NULL);
85  *
86  * This function does the same as standard atof(3), but does not take
87  * locale in account. That means, the decimal delimiter is always '.'
88  * (decimal point). Use CPLAtofDelim() function if you want to specify
89  * custom delimiter.
90  *
91  * IMPORTANT NOTE.
92  * Existance of this function does not mean you should always use it.
93  * Sometimes you should use standard locale aware atof(3) and its family. When
94  * you need to process the user's input (for example, command line parameters)
95  * use atof(3), because user works in localized environment and her input will
96  * be done accordingly the locale set. In particular that means we should not
97  * make assumptions about character used as decimal delimiter, it can be
98  * either "." or ",".
99  * But when you are parsing some ASCII file in predefined format, you most
100  * likely need CPLAtof(), because such files distributed across the systems
101  * with different locales and floating point representation shoudl be
102  * considered as a part of file format. If the format uses "." as a delimiter
103  * the same character must be used when parsing number regardless of actual
104  * locale setting.
105  *
106  * @param nptr Pointer to string to convert.
107  *
108  * @return Converted value, if any.
109  */
CPLAtof(const char * nptr)110 double CPLAtof(const char *nptr)
111 {
112   return CPLStrtod(nptr, 0);
113 }
114 
115 /************************************************************************/
116 /*                              CPLAtofM()                              */
117 /************************************************************************/
118 
119 /**
120  * Converts ASCII string to floating point number using any numeric locale.
121  *
122  * This function converts the initial portion of the string pointed to
123  * by nptr to double floating point representation. This function does the
124  * same as standard atof(), but it allows a variety of locale representations.
125  * That is it supports numeric values with either a comma or a period for
126  * the decimal delimiter.
127  *
128  * PS. The M stands for Multi-lingual.
129  *
130  * @param nptr The string to convert.
131  *
132  * @return Converted value, if any.  Zero on failure.
133  */
134 
CPLAtofM(const char * nptr)135 double CPLAtofM( const char *nptr )
136 
137 {
138     int i;
139     const static int nMaxSearch = 50;
140 
141     for( i = 0; i < nMaxSearch; i++ )
142     {
143         if( nptr[i] == ',' )
144             return CPLStrtodDelim( nptr, 0, ',' );
145         else if( nptr[i] == '.' || nptr[i] == '\0' )
146             return CPLStrtodDelim( nptr, 0, '.' );
147     }
148 
149     return CPLStrtodDelim( nptr, 0, '.' );
150 }
151 
152 /************************************************************************/
153 /*                          CPLStrtodDelim()                            */
154 /************************************************************************/
155 
CPLReplacePointByLocalePoint(char * pszNumber,char point)156 static void CPLReplacePointByLocalePoint(char* pszNumber, char point)
157 {
158 #if defined(WIN32CE)
159     static char byPoint = 0;
160     if (byPoint == 0)
161     {
162         char szBuf[16];
163         sprintf(szBuf, "%.1f", 1.0);
164         byPoint = szBuf[1];
165     }
166     if (point != byPoint)
167     {
168         int     i = 0;
169 
170         while ( pszNumber[i] )
171         {
172             if ( pszNumber[i] == point )
173             {
174                 pszNumber[i] = byPoint;
175                 break;
176             }
177             i++;
178         }
179     }
180 #else
181     struct lconv *poLconv = localeconv();
182     if ( poLconv
183          && poLconv->decimal_point
184          && strlen(poLconv->decimal_point) > 0 )
185     {
186         int     i = 0;
187         char    byPoint = poLconv->decimal_point[0];
188 
189         if (point != byPoint)
190         {
191             while ( pszNumber[i] )
192             {
193                 if ( pszNumber[i] == point )
194                 {
195                     pszNumber[i] = byPoint;
196                     break;
197                 }
198                 i++;
199             }
200         }
201     }
202 #endif
203 }
204 
205 
206 /**
207  * Converts ASCII string to floating point number using specified delimiter.
208  *
209  * This function converts the initial portion of the string pointed to
210  * by nptr to double floating point representation. This function does the
211  * same as standard strtod(3), but does not take locale in account. Instead of
212  * locale defined decimal delimiter you can specify your own one. Also see
213  * notes for CPLAtof() function.
214  *
215  * @param nptr Pointer to string to convert.
216  * @param endptr If is not NULL, a pointer to the character after the last
217  * character used in the conversion is stored in the location referenced
218  * by endptr.
219  * @param point Decimal delimiter.
220  *
221  * @return Converted value, if any.
222  */
CPLStrtodDelim(const char * nptr,char ** endptr,char point)223 double CPLStrtodDelim(const char *nptr, char **endptr, char point)
224 {
225 /* -------------------------------------------------------------------- */
226 /*  We are implementing a simple method here: copy the input string     */
227 /*  into the temporary buffer, replace the specified decimal delimiter  */
228 /*  with the one, taken from locale settings and use standard strtod()  */
229 /*  on that buffer.                                                     */
230 /* -------------------------------------------------------------------- */
231     char        *pszNumber = CPLStrdup( nptr );
232     double      dfValue;
233     int         nError;
234 
235     CPLReplacePointByLocalePoint(pszNumber, point);
236 
237     dfValue = strtod( pszNumber, endptr );
238     nError = errno;
239 
240     if ( endptr )
241         *endptr = (char *)nptr + (*endptr - pszNumber);
242 
243     CPLFree( pszNumber );
244 
245     errno = nError;
246     return dfValue;
247 }
248 
249 /************************************************************************/
250 /*                             CPLStrtod()                              */
251 /************************************************************************/
252 
253 /**
254  * Converts ASCII string to floating point number.
255  *
256  * This function converts the initial portion of the string pointed to
257  * by nptr to double floating point representation. This function does the
258  * same as standard strtod(3), but does not take locale in account. That
259  * means, the decimal delimiter is always '.' (decimal point). Use
260  * CPLStrtodDelim() function if you want to specify custom delimiter. Also
261  * see notes for CPLAtof() function.
262  *
263  * @param nptr Pointer to string to convert.
264  * @param endptr If is not NULL, a pointer to the character after the last
265  * character used in the conversion is stored in the location referenced
266  * by endptr.
267  *
268  * @return Converted value, if any.
269  */
CPLStrtod(const char * nptr,char ** endptr)270 double CPLStrtod(const char *nptr, char **endptr)
271 {
272     return CPLStrtodDelim(nptr, endptr, '.');
273 }
274 
275 /************************************************************************/
276 /*                          CPLStrtofDelim()                            */
277 /************************************************************************/
278 
279 /**
280  * Converts ASCII string to floating point number using specified delimiter.
281  *
282  * This function converts the initial portion of the string pointed to
283  * by nptr to single floating point representation. This function does the
284  * same as standard strtof(3), but does not take locale in account. Instead of
285  * locale defined decimal delimiter you can specify your own one. Also see
286  * notes for CPLAtof() function.
287  *
288  * @param nptr Pointer to string to convert.
289  * @param endptr If is not NULL, a pointer to the character after the last
290  * character used in the conversion is stored in the location referenced
291  * by endptr.
292  * @param point Decimal delimiter.
293  *
294  * @return Converted value, if any.
295  */
CPLStrtofDelim(const char * nptr,char ** endptr,char point)296 float CPLStrtofDelim(const char *nptr, char **endptr, char point)
297 {
298 #if defined(HAVE_STRTOF)
299 /* -------------------------------------------------------------------- */
300 /*  We are implementing a simple method here: copy the input string     */
301 /*  into the temporary buffer, replace the specified decimal delimiter  */
302 /*  with the one, taken from locale settings and use standard strtof()  */
303 /*  on that buffer.                                                     */
304 /* -------------------------------------------------------------------- */
305 
306     char        *pszNumber = CPLStrdup( nptr );
307     double      dfValue;
308     int         nError;
309 
310     CPLReplacePointByLocalePoint(pszNumber, point);
311 
312     dfValue = strtof( pszNumber, endptr );
313     nError = errno;
314 
315     if ( endptr )
316         *endptr = (char *)nptr + (*endptr - pszNumber);
317 
318     CPLFree( pszNumber );
319 
320     errno = nError;
321     return dfValue;
322 
323 #else
324 
325     return (float)CPLStrtodDelim(nptr, endptr, point);
326 
327 #endif /* HAVE_STRTOF */
328 }
329 
330 /************************************************************************/
331 /*                             CPLStrtof()                              */
332 /************************************************************************/
333 
334 /**
335  * Converts ASCII string to floating point number.
336  *
337  * This function converts the initial portion of the string pointed to
338  * by nptr to single floating point representation. This function does the
339  * same as standard strtof(3), but does not take locale in account. That
340  * means, the decimal delimiter is always '.' (decimal point). Use
341  * CPLStrtofDelim() function if you want to specify custom delimiter. Also
342  * see notes for CPLAtof() function.
343  *
344  * @param nptr Pointer to string to convert.
345  * @param endptr If is not NULL, a pointer to the character after the last
346  * character used in the conversion is stored in the location referenced
347  * by endptr.
348  *
349  * @return Converted value, if any.
350  */
CPLStrtof(const char * nptr,char ** endptr)351 float CPLStrtof(const char *nptr, char **endptr)
352 {
353     return CPLStrtofDelim(nptr, endptr, '.');
354 }
355 /* END OF FILE */
356