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 : */
68 0 : double CPLAtofDelim(const char *nptr, char point)
69 : {
70 0 : 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 : */
110 17424 : double CPLAtof(const char *nptr)
111 : {
112 17424 : 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 :
135 425 : double CPLAtofM( const char *nptr )
136 :
137 : {
138 : int i;
139 : const static int nMaxSearch = 50;
140 :
141 1453 : for( i = 0; i < nMaxSearch; i++ )
142 : {
143 1453 : if( nptr[i] == ',' )
144 9 : return CPLStrtodDelim( nptr, 0, ',' );
145 1444 : else if( nptr[i] == '.' || nptr[i] == '\0' )
146 416 : return CPLStrtodDelim( nptr, 0, '.' );
147 : }
148 :
149 0 : return CPLStrtodDelim( nptr, 0, '.' );
150 : }
151 :
152 : /************************************************************************/
153 : /* CPLStrtodDelim() */
154 : /************************************************************************/
155 :
156 26459 : 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 26459 : struct lconv *poLconv = localeconv();
182 26459 : if ( poLconv
183 : && poLconv->decimal_point
184 : && strlen(poLconv->decimal_point) > 0 )
185 : {
186 26459 : int i = 0;
187 26459 : char byPoint = poLconv->decimal_point[0];
188 :
189 26459 : if (point != byPoint)
190 : {
191 51 : while ( pszNumber[i] )
192 : {
193 42 : if ( pszNumber[i] == point )
194 : {
195 9 : pszNumber[i] = byPoint;
196 9 : break;
197 : }
198 33 : i++;
199 : }
200 : }
201 : }
202 : #endif
203 26459 : }
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 : */
223 26459 : 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 26459 : char *pszNumber = CPLStrdup( nptr );
232 : double dfValue;
233 : int nError;
234 :
235 26459 : CPLReplacePointByLocalePoint(pszNumber, point);
236 :
237 26459 : dfValue = strtod( pszNumber, endptr );
238 26459 : nError = errno;
239 :
240 26459 : if ( endptr )
241 8610 : *endptr = (char *)nptr + (*endptr - pszNumber);
242 :
243 26459 : CPLFree( pszNumber );
244 :
245 26459 : errno = nError;
246 26459 : 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 : */
270 26034 : double CPLStrtod(const char *nptr, char **endptr)
271 : {
272 26034 : 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 : */
296 0 : 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 0 : char *pszNumber = CPLStrdup( nptr );
307 : double dfValue;
308 : int nError;
309 :
310 0 : CPLReplacePointByLocalePoint(pszNumber, point);
311 :
312 0 : dfValue = strtof( pszNumber, endptr );
313 0 : nError = errno;
314 :
315 0 : if ( endptr )
316 0 : *endptr = (char *)nptr + (*endptr - pszNumber);
317 :
318 0 : CPLFree( pszNumber );
319 :
320 0 : errno = nError;
321 0 : 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 : */
351 0 : float CPLStrtof(const char *nptr, char **endptr)
352 : {
353 0 : return CPLStrtofDelim(nptr, endptr, '.');
354 : }
355 : /* END OF FILE */
|