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