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