1 : /******************************************************************************
2 : * $Id: cpl_strtod.cpp 24899 2012-09-03 11:42:58Z rouault $
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 24899 2012-09-03 11:42:58Z rouault $");
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 : #ifndef NAN
47 : # ifdef HUGE_VAL
48 : # define NAN (HUGE_VAL * 0.0)
49 : # else
50 :
51 : static float CPLNaN(void)
52 : {
53 : float fNan;
54 : int nNan = 0x7FC00000;
55 : memcpy(&fNan, &nNan, 4);
56 : return fNan;
57 : }
58 :
59 : # define NAN CPLNan()
60 : # endif
61 : #endif
62 :
63 : #ifndef INFINITY
64 : static CPL_INLINE double CPLInfinity(void)
65 : {
66 : static double ZERO = 0;
67 : return 1.0 / ZERO; /* MSVC doesn't like 1.0 / 0.0 */
68 : }
69 : #define INFINITY CPLInfinity()
70 : #endif
71 :
72 : /************************************************************************/
73 : /* CPLAtofDelim() */
74 : /************************************************************************/
75 :
76 : /**
77 : * Converts ASCII string to floating point number.
78 : *
79 : * This function converts the initial portion of the string pointed to
80 : * by nptr to double floating point representation. The behaviour is the
81 : * same as
82 : *
83 : * CPLStrtodDelim(nptr, (char **)NULL, point);
84 : *
85 : * This function does the same as standard atof(3), but does not take locale
86 : * in account. Instead of locale defined decimal delimiter you can specify
87 : * your own one. Also see notes for CPLAtof() function.
88 : *
89 : * @param nptr Pointer to string to convert.
90 : * @param point Decimal delimiter.
91 : *
92 : * @return Converted value, if any.
93 : */
94 0 : double CPLAtofDelim(const char *nptr, char point)
95 : {
96 0 : return CPLStrtodDelim(nptr, 0, point);
97 : }
98 :
99 : /************************************************************************/
100 : /* CPLAtof() */
101 : /************************************************************************/
102 :
103 : /**
104 : * Converts ASCII string to floating point number.
105 : *
106 : * This function converts the initial portion of the string pointed to
107 : * by nptr to double floating point representation. The behaviour is the
108 : * same as
109 : *
110 : * CPLStrtod(nptr, (char **)NULL);
111 : *
112 : * This function does the same as standard atof(3), but does not take
113 : * locale in account. That means, the decimal delimiter is always '.'
114 : * (decimal point). Use CPLAtofDelim() function if you want to specify
115 : * custom delimiter.
116 : *
117 : * IMPORTANT NOTE.
118 : * Existance of this function does not mean you should always use it.
119 : * Sometimes you should use standard locale aware atof(3) and its family. When
120 : * you need to process the user's input (for example, command line parameters)
121 : * use atof(3), because user works in localized environment and her input will
122 : * be done accordingly the locale set. In particular that means we should not
123 : * make assumptions about character used as decimal delimiter, it can be
124 : * either "." or ",".
125 : * But when you are parsing some ASCII file in predefined format, you most
126 : * likely need CPLAtof(), because such files distributed across the systems
127 : * with different locales and floating point representation shoudl be
128 : * considered as a part of file format. If the format uses "." as a delimiter
129 : * the same character must be used when parsing number regardless of actual
130 : * locale setting.
131 : *
132 : * @param nptr Pointer to string to convert.
133 : *
134 : * @return Converted value, if any.
135 : */
136 1486550 : double CPLAtof(const char *nptr)
137 : {
138 1486550 : return CPLStrtod(nptr, 0);
139 : }
140 :
141 : /************************************************************************/
142 : /* CPLAtofM() */
143 : /************************************************************************/
144 :
145 : /**
146 : * Converts ASCII string to floating point number using any numeric locale.
147 : *
148 : * This function converts the initial portion of the string pointed to
149 : * by nptr to double floating point representation. This function does the
150 : * same as standard atof(), but it allows a variety of locale representations.
151 : * That is it supports numeric values with either a comma or a period for
152 : * the decimal delimiter.
153 : *
154 : * PS. The M stands for Multi-lingual.
155 : *
156 : * @param nptr The string to convert.
157 : *
158 : * @return Converted value, if any. Zero on failure.
159 : */
160 :
161 264177 : double CPLAtofM( const char *nptr )
162 :
163 : {
164 : int i;
165 : const static int nMaxSearch = 50;
166 :
167 1517292 : for( i = 0; i < nMaxSearch; i++ )
168 : {
169 1517292 : if( nptr[i] == ',' )
170 189 : return CPLStrtodDelim( nptr, 0, ',' );
171 1517103 : else if( nptr[i] == '.' || nptr[i] == '\0' )
172 263988 : return CPLStrtodDelim( nptr, 0, '.' );
173 : }
174 :
175 0 : return CPLStrtodDelim( nptr, 0, '.' );
176 : }
177 :
178 : /************************************************************************/
179 : /* CPLReplacePointByLocalePoint() */
180 : /************************************************************************/
181 :
182 2310874 : static char* CPLReplacePointByLocalePoint(const char* pszNumber, char point)
183 : {
184 : #if defined(WIN32CE) || defined(__ANDROID__)
185 : static char byPoint = 0;
186 : if (byPoint == 0)
187 : {
188 : char szBuf[16];
189 : sprintf(szBuf, "%.1f", 1.0);
190 : byPoint = szBuf[1];
191 : }
192 : if (point != byPoint)
193 : {
194 : const char* pszPoint = strchr(pszNumber, point);
195 : if (pszPoint)
196 : {
197 : char* pszNew = CPLStrdup(pszNumber);
198 : pszNew[pszPoint - pszNumber] = byPoint;
199 : return pszNew;
200 : }
201 : }
202 : #else
203 2310874 : struct lconv *poLconv = localeconv();
204 2310874 : if ( poLconv
205 : && poLconv->decimal_point
206 : && strlen(poLconv->decimal_point) > 0 )
207 : {
208 2310874 : char byPoint = poLconv->decimal_point[0];
209 :
210 2310874 : if (point != byPoint)
211 : {
212 189 : const char* pszPoint = strchr(pszNumber, point);
213 189 : if (pszPoint)
214 : {
215 189 : char* pszNew = CPLStrdup(pszNumber);
216 189 : pszNew[pszPoint - pszNumber] = byPoint;
217 189 : return pszNew;
218 : }
219 : }
220 : }
221 : #endif
222 2310685 : return (char*) pszNumber;
223 : }
224 :
225 : /************************************************************************/
226 : /* CPLStrtodDelim() */
227 : /************************************************************************/
228 :
229 : /**
230 : * Converts ASCII string to floating point number using specified delimiter.
231 : *
232 : * This function converts the initial portion of the string pointed to
233 : * by nptr to double floating point representation. This function does the
234 : * same as standard strtod(3), but does not take locale in account. Instead of
235 : * locale defined decimal delimiter you can specify your own one. Also see
236 : * notes for CPLAtof() function.
237 : *
238 : * @param nptr Pointer to string to convert.
239 : * @param endptr If is not NULL, a pointer to the character after the last
240 : * character used in the conversion is stored in the location referenced
241 : * by endptr.
242 : * @param point Decimal delimiter.
243 : *
244 : * @return Converted value, if any.
245 : */
246 2310900 : double CPLStrtodDelim(const char *nptr, char **endptr, char point)
247 : {
248 5095471 : while( *nptr == ' ' )
249 473671 : nptr ++;
250 :
251 2310900 : if (nptr[0] == '-')
252 : {
253 108312 : if (strcmp(nptr, "-1.#QNAN") == 0 ||
254 : strcmp(nptr, "-1.#IND") == 0)
255 0 : return NAN;
256 :
257 108312 : if (strcmp(nptr,"-inf") == 0 ||
258 : strcmp(nptr,"-1.#INF") == 0)
259 2 : return -INFINITY;
260 : }
261 2202588 : else if (nptr[0] == '1')
262 : {
263 384716 : if (strcmp(nptr, "1.#QNAN") == 0)
264 0 : return NAN;
265 384716 : if (strcmp (nptr,"1.#INF") == 0)
266 1 : return INFINITY;
267 : }
268 1817872 : else if (nptr[0] == 'i' && strcmp(nptr,"inf") == 0)
269 1 : return INFINITY;
270 1817871 : else if (nptr[0] == 'n' && strcmp(nptr,"nan") == 0)
271 22 : return NAN;
272 :
273 : /* -------------------------------------------------------------------- */
274 : /* We are implementing a simple method here: copy the input string */
275 : /* into the temporary buffer, replace the specified decimal delimiter */
276 : /* with the one, taken from locale settings and use standard strtod() */
277 : /* on that buffer. */
278 : /* -------------------------------------------------------------------- */
279 : double dfValue;
280 : int nError;
281 :
282 2310874 : char* pszNumber = CPLReplacePointByLocalePoint(nptr, point);
283 :
284 2310874 : dfValue = strtod( pszNumber, endptr );
285 2310874 : nError = errno;
286 :
287 2310874 : if ( endptr )
288 560171 : *endptr = (char *)nptr + (*endptr - pszNumber);
289 :
290 2310874 : if (pszNumber != (char*) nptr)
291 189 : CPLFree( pszNumber );
292 :
293 2310874 : errno = nError;
294 2310874 : return dfValue;
295 : }
296 :
297 : /************************************************************************/
298 : /* CPLStrtod() */
299 : /************************************************************************/
300 :
301 : /**
302 : * Converts ASCII string to floating point number.
303 : *
304 : * This function converts the initial portion of the string pointed to
305 : * by nptr to double floating point representation. This function does the
306 : * same as standard strtod(3), but does not take locale in account. That
307 : * means, the decimal delimiter is always '.' (decimal point). Use
308 : * CPLStrtodDelim() function if you want to specify custom delimiter. Also
309 : * see notes for CPLAtof() function.
310 : *
311 : * @param nptr Pointer to string to convert.
312 : * @param endptr If is not NULL, a pointer to the character after the last
313 : * character used in the conversion is stored in the location referenced
314 : * by endptr.
315 : *
316 : * @return Converted value, if any.
317 : */
318 2046723 : double CPLStrtod(const char *nptr, char **endptr)
319 : {
320 2046723 : return CPLStrtodDelim(nptr, endptr, '.');
321 : }
322 :
323 : /************************************************************************/
324 : /* CPLStrtofDelim() */
325 : /************************************************************************/
326 :
327 : /**
328 : * Converts ASCII string to floating point number using specified delimiter.
329 : *
330 : * This function converts the initial portion of the string pointed to
331 : * by nptr to single floating point representation. This function does the
332 : * same as standard strtof(3), but does not take locale in account. Instead of
333 : * locale defined decimal delimiter you can specify your own one. Also see
334 : * notes for CPLAtof() function.
335 : *
336 : * @param nptr Pointer to string to convert.
337 : * @param endptr If is not NULL, a pointer to the character after the last
338 : * character used in the conversion is stored in the location referenced
339 : * by endptr.
340 : * @param point Decimal delimiter.
341 : *
342 : * @return Converted value, if any.
343 : */
344 0 : float CPLStrtofDelim(const char *nptr, char **endptr, char point)
345 : {
346 : #if defined(HAVE_STRTOF)
347 : /* -------------------------------------------------------------------- */
348 : /* We are implementing a simple method here: copy the input string */
349 : /* into the temporary buffer, replace the specified decimal delimiter */
350 : /* with the one, taken from locale settings and use standard strtof() */
351 : /* on that buffer. */
352 : /* -------------------------------------------------------------------- */
353 :
354 : double dfValue;
355 : int nError;
356 :
357 0 : char* pszNumber = CPLReplacePointByLocalePoint(nptr, point);
358 :
359 0 : dfValue = strtof( pszNumber, endptr );
360 0 : nError = errno;
361 :
362 0 : if ( endptr )
363 0 : *endptr = (char *)nptr + (*endptr - pszNumber);
364 :
365 0 : if (pszNumber != (char*) nptr)
366 0 : CPLFree( pszNumber );
367 :
368 0 : errno = nError;
369 0 : return dfValue;
370 :
371 : #else
372 :
373 : return (float)CPLStrtodDelim(nptr, endptr, point);
374 :
375 : #endif /* HAVE_STRTOF */
376 : }
377 :
378 : /************************************************************************/
379 : /* CPLStrtof() */
380 : /************************************************************************/
381 :
382 : /**
383 : * Converts ASCII string to floating point number.
384 : *
385 : * This function converts the initial portion of the string pointed to
386 : * by nptr to single floating point representation. This function does the
387 : * same as standard strtof(3), but does not take locale in account. That
388 : * means, the decimal delimiter is always '.' (decimal point). Use
389 : * CPLStrtofDelim() function if you want to specify custom delimiter. Also
390 : * see notes for CPLAtof() function.
391 : *
392 : * @param nptr Pointer to string to convert.
393 : * @param endptr If is not NULL, a pointer to the character after the last
394 : * character used in the conversion is stored in the location referenced
395 : * by endptr.
396 : *
397 : * @return Converted value, if any.
398 : */
399 0 : float CPLStrtof(const char *nptr, char **endptr)
400 : {
401 0 : return CPLStrtofDelim(nptr, endptr, '.');
402 : }
403 :
404 : /* END OF FILE */
|