1 : /******************************************************************************
2 : * $Id: cpl_strtod.cpp 23937 2012-02-10 20:23:24Z 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 23937 2012-02-10 20:23:24Z 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 2197158 : double CPLAtof(const char *nptr)
137 : {
138 2197158 : 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 697504 : double CPLAtofM( const char *nptr )
162 :
163 : {
164 : int i;
165 : const static int nMaxSearch = 50;
166 :
167 4280642 : for( i = 0; i < nMaxSearch; i++ )
168 : {
169 4280642 : if( nptr[i] == ',' )
170 378 : return CPLStrtodDelim( nptr, 0, ',' );
171 4280264 : else if( nptr[i] == '.' || nptr[i] == '\0' )
172 697126 : return CPLStrtodDelim( nptr, 0, '.' );
173 : }
174 :
175 0 : return CPLStrtodDelim( nptr, 0, '.' );
176 : }
177 :
178 : /************************************************************************/
179 : /* CPLReplacePointByLocalePoint() */
180 : /************************************************************************/
181 :
182 2914540 : 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 2914540 : struct lconv *poLconv = localeconv();
204 2914540 : if ( poLconv
205 : && poLconv->decimal_point
206 : && strlen(poLconv->decimal_point) > 0 )
207 : {
208 2914540 : char byPoint = poLconv->decimal_point[0];
209 :
210 2914540 : if (point != byPoint)
211 : {
212 378 : const char* pszPoint = strchr(pszNumber, point);
213 378 : if (pszPoint)
214 : {
215 378 : char* pszNew = CPLStrdup(pszNumber);
216 378 : pszNew[pszPoint - pszNumber] = byPoint;
217 378 : return pszNew;
218 : }
219 : }
220 : }
221 : #endif
222 2914162 : 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 2914586 : double CPLStrtodDelim(const char *nptr, char **endptr, char point)
247 : {
248 2914586 : if (nptr[0] == '-')
249 : {
250 127181 : if (strcmp(nptr, "-1.#QNAN") == 0 ||
251 : strcmp(nptr, "-1.#IND") == 0)
252 0 : return NAN;
253 :
254 127181 : if (strcmp(nptr,"-inf") == 0 ||
255 : strcmp(nptr,"-1.#INF") == 0)
256 4 : return -INFINITY;
257 : }
258 2787405 : else if (nptr[0] == '1')
259 : {
260 538079 : if (strcmp(nptr, "1.#QNAN") == 0)
261 0 : return NAN;
262 538079 : if (strcmp (nptr,"1.#INF") == 0)
263 2 : return INFINITY;
264 : }
265 2249326 : else if (nptr[0] == 'i' && strcmp(nptr,"inf") == 0)
266 2 : return INFINITY;
267 2249324 : else if (nptr[0] == 'n' && strcmp(nptr,"nan") == 0)
268 38 : return NAN;
269 :
270 : /* -------------------------------------------------------------------- */
271 : /* We are implementing a simple method here: copy the input string */
272 : /* into the temporary buffer, replace the specified decimal delimiter */
273 : /* with the one, taken from locale settings and use standard strtod() */
274 : /* on that buffer. */
275 : /* -------------------------------------------------------------------- */
276 : double dfValue;
277 : int nError;
278 :
279 2914540 : char* pszNumber = CPLReplacePointByLocalePoint(nptr, point);
280 :
281 2914540 : dfValue = strtod( pszNumber, endptr );
282 2914540 : nError = errno;
283 :
284 2914540 : if ( endptr )
285 19920 : *endptr = (char *)nptr + (*endptr - pszNumber);
286 :
287 2914540 : if (pszNumber != (char*) nptr)
288 378 : CPLFree( pszNumber );
289 :
290 2914540 : errno = nError;
291 2914540 : return dfValue;
292 : }
293 :
294 : /************************************************************************/
295 : /* CPLStrtod() */
296 : /************************************************************************/
297 :
298 : /**
299 : * Converts ASCII string to floating point number.
300 : *
301 : * This function converts the initial portion of the string pointed to
302 : * by nptr to double floating point representation. This function does the
303 : * same as standard strtod(3), but does not take locale in account. That
304 : * means, the decimal delimiter is always '.' (decimal point). Use
305 : * CPLStrtodDelim() function if you want to specify custom delimiter. Also
306 : * see notes for CPLAtof() function.
307 : *
308 : * @param nptr Pointer to string to convert.
309 : * @param endptr If is not NULL, a pointer to the character after the last
310 : * character used in the conversion is stored in the location referenced
311 : * by endptr.
312 : *
313 : * @return Converted value, if any.
314 : */
315 2217082 : double CPLStrtod(const char *nptr, char **endptr)
316 : {
317 2217082 : return CPLStrtodDelim(nptr, endptr, '.');
318 : }
319 :
320 : /************************************************************************/
321 : /* CPLStrtofDelim() */
322 : /************************************************************************/
323 :
324 : /**
325 : * Converts ASCII string to floating point number using specified delimiter.
326 : *
327 : * This function converts the initial portion of the string pointed to
328 : * by nptr to single floating point representation. This function does the
329 : * same as standard strtof(3), but does not take locale in account. Instead of
330 : * locale defined decimal delimiter you can specify your own one. Also see
331 : * notes for CPLAtof() function.
332 : *
333 : * @param nptr Pointer to string to convert.
334 : * @param endptr If is not NULL, a pointer to the character after the last
335 : * character used in the conversion is stored in the location referenced
336 : * by endptr.
337 : * @param point Decimal delimiter.
338 : *
339 : * @return Converted value, if any.
340 : */
341 0 : float CPLStrtofDelim(const char *nptr, char **endptr, char point)
342 : {
343 : #if defined(HAVE_STRTOF)
344 : /* -------------------------------------------------------------------- */
345 : /* We are implementing a simple method here: copy the input string */
346 : /* into the temporary buffer, replace the specified decimal delimiter */
347 : /* with the one, taken from locale settings and use standard strtof() */
348 : /* on that buffer. */
349 : /* -------------------------------------------------------------------- */
350 :
351 : double dfValue;
352 : int nError;
353 :
354 0 : char* pszNumber = CPLReplacePointByLocalePoint(nptr, point);
355 :
356 0 : dfValue = strtof( pszNumber, endptr );
357 0 : nError = errno;
358 :
359 0 : if ( endptr )
360 0 : *endptr = (char *)nptr + (*endptr - pszNumber);
361 :
362 0 : if (pszNumber != (char*) nptr)
363 0 : CPLFree( pszNumber );
364 :
365 0 : errno = nError;
366 0 : return dfValue;
367 :
368 : #else
369 :
370 : return (float)CPLStrtodDelim(nptr, endptr, point);
371 :
372 : #endif /* HAVE_STRTOF */
373 : }
374 :
375 : /************************************************************************/
376 : /* CPLStrtof() */
377 : /************************************************************************/
378 :
379 : /**
380 : * Converts ASCII string to floating point number.
381 : *
382 : * This function converts the initial portion of the string pointed to
383 : * by nptr to single floating point representation. This function does the
384 : * same as standard strtof(3), but does not take locale in account. That
385 : * means, the decimal delimiter is always '.' (decimal point). Use
386 : * CPLStrtofDelim() function if you want to specify custom delimiter. Also
387 : * see notes for CPLAtof() function.
388 : *
389 : * @param nptr Pointer to string to convert.
390 : * @param endptr If is not NULL, a pointer to the character after the last
391 : * character used in the conversion is stored in the location referenced
392 : * by endptr.
393 : *
394 : * @return Converted value, if any.
395 : */
396 0 : float CPLStrtof(const char *nptr, char **endptr)
397 : {
398 0 : return CPLStrtofDelim(nptr, endptr, '.');
399 : }
400 :
401 : /* END OF FILE */
|