1 : /******************************************************************************
2 : * $Id: cpl_strtod.cpp 23556 2011-12-12 21:49:34Z 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 23556 2011-12-12 21:49:34Z 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 937384 : double CPLAtof(const char *nptr)
128 : {
129 937384 : 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 356876 : double CPLAtofM( const char *nptr )
153 :
154 : {
155 : int i;
156 : const static int nMaxSearch = 50;
157 :
158 2164050 : for( i = 0; i < nMaxSearch; i++ )
159 : {
160 2164050 : if( nptr[i] == ',' )
161 189 : return CPLStrtodDelim( nptr, 0, ',' );
162 2163861 : else if( nptr[i] == '.' || nptr[i] == '\0' )
163 356687 : return CPLStrtodDelim( nptr, 0, '.' );
164 : }
165 :
166 0 : return CPLStrtodDelim( nptr, 0, '.' );
167 : }
168 :
169 : /************************************************************************/
170 : /* CPLReplacePointByLocalePoint() */
171 : /************************************************************************/
172 :
173 1303752 : static char* CPLReplacePointByLocalePoint(const char* pszNumber, char point)
174 : {
175 : #if defined(WIN32CE) || defined(__ANDROID__)
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 : const char* pszPoint = strchr(pszNumber, point);
186 : if (pszPoint)
187 : {
188 : char* pszNew = CPLStrdup(pszNumber);
189 : pszNew[pszPoint - pszNumber] = byPoint;
190 : return pszNew;
191 : }
192 : }
193 : #else
194 1303752 : struct lconv *poLconv = localeconv();
195 1303752 : if ( poLconv
196 : && poLconv->decimal_point
197 : && strlen(poLconv->decimal_point) > 0 )
198 : {
199 1303752 : char byPoint = poLconv->decimal_point[0];
200 :
201 1303752 : if (point != byPoint)
202 : {
203 189 : const char* pszPoint = strchr(pszNumber, point);
204 189 : if (pszPoint)
205 : {
206 189 : char* pszNew = CPLStrdup(pszNumber);
207 189 : pszNew[pszPoint - pszNumber] = byPoint;
208 189 : return pszNew;
209 : }
210 : }
211 : }
212 : #endif
213 1303563 : return (char*) pszNumber;
214 : }
215 :
216 : /************************************************************************/
217 : /* CPLStrtodDelim() */
218 : /************************************************************************/
219 :
220 : /**
221 : * Converts ASCII string to floating point number using specified delimiter.
222 : *
223 : * This function converts the initial portion of the string pointed to
224 : * by nptr to double floating point representation. This function does the
225 : * same as standard strtod(3), but does not take locale in account. Instead of
226 : * locale defined decimal delimiter you can specify your own one. Also see
227 : * notes for CPLAtof() function.
228 : *
229 : * @param nptr Pointer to string to convert.
230 : * @param endptr If is not NULL, a pointer to the character after the last
231 : * character used in the conversion is stored in the location referenced
232 : * by endptr.
233 : * @param point Decimal delimiter.
234 : *
235 : * @return Converted value, if any.
236 : */
237 1303951 : double CPLStrtodDelim(const char *nptr, char **endptr, char point)
238 : {
239 1303951 : if (EQUAL(nptr,"nan") || EQUAL(nptr, "1.#QNAN") ||
240 : EQUAL(nptr, "-1.#QNAN") || EQUAL(nptr, "-1.#IND"))
241 199 : return NAN;
242 :
243 : /* -------------------------------------------------------------------- */
244 : /* We are implementing a simple method here: copy the input string */
245 : /* into the temporary buffer, replace the specified decimal delimiter */
246 : /* with the one, taken from locale settings and use standard strtod() */
247 : /* on that buffer. */
248 : /* -------------------------------------------------------------------- */
249 : double dfValue;
250 : int nError;
251 :
252 1303752 : char* pszNumber = CPLReplacePointByLocalePoint(nptr, point);
253 :
254 1303752 : dfValue = strtod( pszNumber, endptr );
255 1303752 : nError = errno;
256 :
257 1303752 : if ( endptr )
258 9691 : *endptr = (char *)nptr + (*endptr - pszNumber);
259 :
260 1303752 : if (pszNumber != (char*) nptr)
261 189 : CPLFree( pszNumber );
262 :
263 1303752 : errno = nError;
264 1303752 : return dfValue;
265 : }
266 :
267 : /************************************************************************/
268 : /* CPLStrtod() */
269 : /************************************************************************/
270 :
271 : /**
272 : * Converts ASCII string to floating point number.
273 : *
274 : * This function converts the initial portion of the string pointed to
275 : * by nptr to double floating point representation. This function does the
276 : * same as standard strtod(3), but does not take locale in account. That
277 : * means, the decimal delimiter is always '.' (decimal point). Use
278 : * CPLStrtodDelim() function if you want to specify custom delimiter. Also
279 : * see notes for CPLAtof() function.
280 : *
281 : * @param nptr Pointer to string to convert.
282 : * @param endptr If is not NULL, a pointer to the character after the last
283 : * character used in the conversion is stored in the location referenced
284 : * by endptr.
285 : *
286 : * @return Converted value, if any.
287 : */
288 947075 : double CPLStrtod(const char *nptr, char **endptr)
289 : {
290 947075 : return CPLStrtodDelim(nptr, endptr, '.');
291 : }
292 :
293 : /************************************************************************/
294 : /* CPLStrtofDelim() */
295 : /************************************************************************/
296 :
297 : /**
298 : * Converts ASCII string to floating point number using specified delimiter.
299 : *
300 : * This function converts the initial portion of the string pointed to
301 : * by nptr to single floating point representation. This function does the
302 : * same as standard strtof(3), but does not take locale in account. Instead of
303 : * locale defined decimal delimiter you can specify your own one. Also see
304 : * notes for CPLAtof() function.
305 : *
306 : * @param nptr Pointer to string to convert.
307 : * @param endptr If is not NULL, a pointer to the character after the last
308 : * character used in the conversion is stored in the location referenced
309 : * by endptr.
310 : * @param point Decimal delimiter.
311 : *
312 : * @return Converted value, if any.
313 : */
314 0 : float CPLStrtofDelim(const char *nptr, char **endptr, char point)
315 : {
316 : #if defined(HAVE_STRTOF)
317 : /* -------------------------------------------------------------------- */
318 : /* We are implementing a simple method here: copy the input string */
319 : /* into the temporary buffer, replace the specified decimal delimiter */
320 : /* with the one, taken from locale settings and use standard strtof() */
321 : /* on that buffer. */
322 : /* -------------------------------------------------------------------- */
323 :
324 : double dfValue;
325 : int nError;
326 :
327 0 : char* pszNumber = CPLReplacePointByLocalePoint(nptr, point);
328 :
329 0 : dfValue = strtof( pszNumber, endptr );
330 0 : nError = errno;
331 :
332 0 : if ( endptr )
333 0 : *endptr = (char *)nptr + (*endptr - pszNumber);
334 :
335 0 : if (pszNumber != (char*) nptr)
336 0 : CPLFree( pszNumber );
337 :
338 0 : errno = nError;
339 0 : return dfValue;
340 :
341 : #else
342 :
343 : return (float)CPLStrtodDelim(nptr, endptr, point);
344 :
345 : #endif /* HAVE_STRTOF */
346 : }
347 :
348 : /************************************************************************/
349 : /* CPLStrtof() */
350 : /************************************************************************/
351 :
352 : /**
353 : * Converts ASCII string to floating point number.
354 : *
355 : * This function converts the initial portion of the string pointed to
356 : * by nptr to single floating point representation. This function does the
357 : * same as standard strtof(3), but does not take locale in account. That
358 : * means, the decimal delimiter is always '.' (decimal point). Use
359 : * CPLStrtofDelim() function if you want to specify custom delimiter. Also
360 : * see notes for CPLAtof() function.
361 : *
362 : * @param nptr Pointer to string to convert.
363 : * @param endptr If is not NULL, a pointer to the character after the last
364 : * character used in the conversion is stored in the location referenced
365 : * by endptr.
366 : *
367 : * @return Converted value, if any.
368 : */
369 0 : float CPLStrtof(const char *nptr, char **endptr)
370 : {
371 0 : return CPLStrtofDelim(nptr, endptr, '.');
372 : }
373 :
374 : /* END OF FILE */
|