~ubuntu-branches/ubuntu/maverick/python3.1/maverick

« back to all changes in this revision

Viewing changes to Python/pystrtod.c

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2009-03-23 00:01:27 UTC
  • Revision ID: james.westby@ubuntu.com-20090323000127-5fstfxju4ufrhthq
Tags: upstream-3.1~a1+20090322
ImportĀ upstreamĀ versionĀ 3.1~a1+20090322

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; c-file-style: "python" -*- */
 
2
 
 
3
#include <Python.h>
 
4
#include <locale.h>
 
5
 
 
6
/* ascii character tests (as opposed to locale tests) */
 
7
#define ISSPACE(c)  ((c) == ' ' || (c) == '\f' || (c) == '\n' || \
 
8
                     (c) == '\r' || (c) == '\t' || (c) == '\v')
 
9
#define ISDIGIT(c)  ((c) >= '0' && (c) <= '9')
 
10
 
 
11
 
 
12
/**
 
13
 * PyOS_ascii_strtod:
 
14
 * @nptr:    the string to convert to a numeric value.
 
15
 * @endptr:  if non-%NULL, it returns the character after
 
16
 *           the last character used in the conversion.
 
17
 * 
 
18
 * Converts a string to a #gdouble value.
 
19
 * This function behaves like the standard strtod() function
 
20
 * does in the C locale. It does this without actually
 
21
 * changing the current locale, since that would not be
 
22
 * thread-safe.
 
23
 *
 
24
 * This function is typically used when reading configuration
 
25
 * files or other non-user input that should be locale independent.
 
26
 * To handle input from the user you should normally use the
 
27
 * locale-sensitive system strtod() function.
 
28
 *
 
29
 * If the correct value would cause overflow, plus or minus %HUGE_VAL
 
30
 * is returned (according to the sign of the value), and %ERANGE is
 
31
 * stored in %errno. If the correct value would cause underflow,
 
32
 * zero is returned and %ERANGE is stored in %errno.
 
33
 * If memory allocation fails, %ENOMEM is stored in %errno.
 
34
 * 
 
35
 * This function resets %errno before calling strtod() so that
 
36
 * you can reliably detect overflow and underflow.
 
37
 *
 
38
 * Return value: the #gdouble value.
 
39
 **/
 
40
double
 
41
PyOS_ascii_strtod(const char *nptr, char **endptr)
 
42
{
 
43
        char *fail_pos;
 
44
        double val = -1.0;
 
45
        struct lconv *locale_data;
 
46
        const char *decimal_point;
 
47
        size_t decimal_point_len;
 
48
        const char *p, *decimal_point_pos;
 
49
        const char *end = NULL; /* Silence gcc */
 
50
        const char *digits_pos = NULL;
 
51
        int negate = 0;
 
52
 
 
53
        assert(nptr != NULL);
 
54
 
 
55
        fail_pos = NULL;
 
56
 
 
57
        locale_data = localeconv();
 
58
        decimal_point = locale_data->decimal_point;
 
59
        decimal_point_len = strlen(decimal_point);
 
60
 
 
61
        assert(decimal_point_len != 0);
 
62
 
 
63
        decimal_point_pos = NULL;
 
64
 
 
65
        /* We process any leading whitespace and the optional sign manually,
 
66
           then pass the remainder to the system strtod.  This ensures that
 
67
           the result of an underflow has the correct sign. (bug #1725)  */
 
68
 
 
69
        p = nptr;
 
70
        /* Skip leading space */
 
71
        while (ISSPACE(*p))
 
72
                p++;
 
73
 
 
74
        /* Process leading sign, if present */
 
75
        if (*p == '-') {
 
76
                negate = 1;
 
77
                p++;
 
78
        } else if (*p == '+') {
 
79
                p++;
 
80
        }
 
81
 
 
82
        /* What's left should begin with a digit, a decimal point, or one of
 
83
           the letters i, I, n, N. It should not begin with 0x or 0X */
 
84
        if ((!ISDIGIT(*p) &&
 
85
             *p != '.' && *p != 'i' && *p != 'I' && *p != 'n' && *p != 'N')
 
86
            ||
 
87
            (*p == '0' && (p[1] == 'x' || p[1] == 'X')))
 
88
        {
 
89
                if (endptr)
 
90
                        *endptr = (char*)nptr;
 
91
                errno = EINVAL;
 
92
                return val;
 
93
        }
 
94
        digits_pos = p;
 
95
 
 
96
        if (decimal_point[0] != '.' || 
 
97
            decimal_point[1] != 0)
 
98
        {
 
99
                while (ISDIGIT(*p))
 
100
                        p++;
 
101
 
 
102
                if (*p == '.')
 
103
                {
 
104
                        decimal_point_pos = p++;
 
105
 
 
106
                        while (ISDIGIT(*p))
 
107
                                p++;
 
108
 
 
109
                        if (*p == 'e' || *p == 'E')
 
110
                                p++;
 
111
                        if (*p == '+' || *p == '-')
 
112
                                p++;
 
113
                        while (ISDIGIT(*p))
 
114
                                p++;
 
115
                        end = p;
 
116
                }
 
117
                else if (strncmp(p, decimal_point, decimal_point_len) == 0)
 
118
                {
 
119
                        /* Python bug #1417699 */
 
120
                        if (endptr)
 
121
                                *endptr = (char*)nptr;
 
122
                        errno = EINVAL;
 
123
                        return val;
 
124
                }
 
125
                /* For the other cases, we need not convert the decimal
 
126
                   point */
 
127
        }
 
128
 
 
129
        /* Set errno to zero, so that we can distinguish zero results
 
130
           and underflows */
 
131
        errno = 0;
 
132
 
 
133
        if (decimal_point_pos)
 
134
        {
 
135
                char *copy, *c;
 
136
 
 
137
                /* We need to convert the '.' to the locale specific decimal
 
138
                   point */
 
139
                copy = (char *)PyMem_MALLOC(end - digits_pos +
 
140
                                            1 + decimal_point_len);
 
141
                if (copy == NULL) {
 
142
                        if (endptr)
 
143
                                *endptr = (char *)nptr;
 
144
                        errno = ENOMEM;
 
145
                        return val;
 
146
                }
 
147
 
 
148
                c = copy;
 
149
                memcpy(c, digits_pos, decimal_point_pos - digits_pos);
 
150
                c += decimal_point_pos - digits_pos;
 
151
                memcpy(c, decimal_point, decimal_point_len);
 
152
                c += decimal_point_len;
 
153
                memcpy(c, decimal_point_pos + 1,
 
154
                       end - (decimal_point_pos + 1));
 
155
                c += end - (decimal_point_pos + 1);
 
156
                *c = 0;
 
157
 
 
158
                val = strtod(copy, &fail_pos);
 
159
 
 
160
                if (fail_pos)
 
161
                {
 
162
                        if (fail_pos > decimal_point_pos)
 
163
                                fail_pos = (char *)digits_pos +
 
164
                                        (fail_pos - copy) -
 
165
                                        (decimal_point_len - 1);
 
166
                        else
 
167
                                fail_pos = (char *)digits_pos +
 
168
                                        (fail_pos - copy);
 
169
                }
 
170
 
 
171
                PyMem_FREE(copy);
 
172
 
 
173
        }
 
174
        else {
 
175
                val = strtod(digits_pos, &fail_pos);
 
176
        }
 
177
 
 
178
        if (fail_pos == digits_pos)
 
179
                fail_pos = (char *)nptr;
 
180
 
 
181
        if (negate && fail_pos != nptr)
 
182
                val = -val;
 
183
 
 
184
        if (endptr)
 
185
                *endptr = fail_pos;
 
186
 
 
187
        return val;
 
188
}
 
189
 
 
190
/* Given a string that may have a decimal point in the current
 
191
   locale, change it back to a dot.  Since the string cannot get
 
192
   longer, no need for a maximum buffer size parameter. */
 
193
Py_LOCAL_INLINE(void)
 
194
change_decimal_from_locale_to_dot(char* buffer)
 
195
{
 
196
        struct lconv *locale_data = localeconv();
 
197
        const char *decimal_point = locale_data->decimal_point;
 
198
 
 
199
        if (decimal_point[0] != '.' || decimal_point[1] != 0) {
 
200
                size_t decimal_point_len = strlen(decimal_point);
 
201
 
 
202
                if (*buffer == '+' || *buffer == '-')
 
203
                        buffer++;
 
204
                while (isdigit(Py_CHARMASK(*buffer)))
 
205
                        buffer++;
 
206
                if (strncmp(buffer, decimal_point, decimal_point_len) == 0) {
 
207
                        *buffer = '.';
 
208
                        buffer++;
 
209
                        if (decimal_point_len > 1) {
 
210
                                /* buffer needs to get smaller */
 
211
                                size_t rest_len = strlen(buffer +
 
212
                                                     (decimal_point_len - 1));
 
213
                                memmove(buffer,
 
214
                                        buffer + (decimal_point_len - 1),
 
215
                                        rest_len);
 
216
                                buffer[rest_len] = 0;
 
217
                        }
 
218
                }
 
219
        }
 
220
}
 
221
 
 
222
 
 
223
/* From the C99 standard, section 7.19.6:
 
224
The exponent always contains at least two digits, and only as many more digits
 
225
as necessary to represent the exponent.
 
226
*/
 
227
#define MIN_EXPONENT_DIGITS 2
 
228
 
 
229
/* Ensure that any exponent, if present, is at least MIN_EXPONENT_DIGITS
 
230
   in length. */
 
231
Py_LOCAL_INLINE(void)
 
232
ensure_minumim_exponent_length(char* buffer, size_t buf_size)
 
233
{
 
234
        char *p = strpbrk(buffer, "eE");
 
235
        if (p && (*(p + 1) == '-' || *(p + 1) == '+')) {
 
236
                char *start = p + 2;
 
237
                int exponent_digit_cnt = 0;
 
238
                int leading_zero_cnt = 0;
 
239
                int in_leading_zeros = 1;
 
240
                int significant_digit_cnt;
 
241
 
 
242
                /* Skip over the exponent and the sign. */
 
243
                p += 2;
 
244
 
 
245
                /* Find the end of the exponent, keeping track of leading
 
246
                   zeros. */
 
247
                while (*p && isdigit(Py_CHARMASK(*p))) {
 
248
                        if (in_leading_zeros && *p == '0')
 
249
                                ++leading_zero_cnt;
 
250
                        if (*p != '0')
 
251
                                in_leading_zeros = 0;
 
252
                        ++p;
 
253
                        ++exponent_digit_cnt;
 
254
                }
 
255
 
 
256
                significant_digit_cnt = exponent_digit_cnt - leading_zero_cnt;
 
257
                if (exponent_digit_cnt == MIN_EXPONENT_DIGITS) {
 
258
                        /* If there are 2 exactly digits, we're done,
 
259
                           regardless of what they contain */
 
260
                }
 
261
                else if (exponent_digit_cnt > MIN_EXPONENT_DIGITS) {
 
262
                        int extra_zeros_cnt;
 
263
 
 
264
                        /* There are more than 2 digits in the exponent.  See
 
265
                           if we can delete some of the leading zeros */
 
266
                        if (significant_digit_cnt < MIN_EXPONENT_DIGITS)
 
267
                                significant_digit_cnt = MIN_EXPONENT_DIGITS;
 
268
                        extra_zeros_cnt = exponent_digit_cnt -
 
269
                                significant_digit_cnt;
 
270
 
 
271
                        /* Delete extra_zeros_cnt worth of characters from the
 
272
                           front of the exponent */
 
273
                        assert(extra_zeros_cnt >= 0);
 
274
 
 
275
                        /* Add one to significant_digit_cnt to copy the
 
276
                           trailing 0 byte, thus setting the length */
 
277
                        memmove(start,
 
278
                                start + extra_zeros_cnt,
 
279
                                significant_digit_cnt + 1);
 
280
                }
 
281
                else {
 
282
                        /* If there are fewer than 2 digits, add zeros
 
283
                           until there are 2, if there's enough room */
 
284
                        int zeros = MIN_EXPONENT_DIGITS - exponent_digit_cnt;
 
285
                        if (start + zeros + exponent_digit_cnt + 1
 
286
                              < buffer + buf_size) {
 
287
                                memmove(start + zeros, start,
 
288
                                        exponent_digit_cnt + 1);
 
289
                                memset(start, '0', zeros);
 
290
                        }
 
291
                }
 
292
        }
 
293
}
 
294
 
 
295
/* Ensure that buffer has a decimal point in it.  The decimal point
 
296
   will not be in the current locale, it will always be '.' */
 
297
Py_LOCAL_INLINE(void)
 
298
ensure_decimal_point(char* buffer, size_t buf_size)
 
299
{
 
300
        int insert_count = 0;
 
301
        char* chars_to_insert;
 
302
 
 
303
        /* search for the first non-digit character */
 
304
        char *p = buffer;
 
305
        if (*p == '-' || *p == '+')
 
306
                /* Skip leading sign, if present.  I think this could only
 
307
                   ever be '-', but it can't hurt to check for both. */
 
308
                ++p;
 
309
        while (*p && isdigit(Py_CHARMASK(*p)))
 
310
                ++p;
 
311
 
 
312
        if (*p == '.') {
 
313
                if (isdigit(Py_CHARMASK(*(p+1)))) {
 
314
                        /* Nothing to do, we already have a decimal
 
315
                           point and a digit after it */
 
316
                }
 
317
                else {
 
318
                        /* We have a decimal point, but no following
 
319
                           digit.  Insert a zero after the decimal. */
 
320
                        ++p;
 
321
                        chars_to_insert = "0";
 
322
                        insert_count = 1;
 
323
                }
 
324
        }
 
325
        else {
 
326
                chars_to_insert = ".0";
 
327
                insert_count = 2;
 
328
        }
 
329
        if (insert_count) {
 
330
                size_t buf_len = strlen(buffer);
 
331
                if (buf_len + insert_count + 1 >= buf_size) {
 
332
                        /* If there is not enough room in the buffer
 
333
                           for the additional text, just skip it.  It's
 
334
                           not worth generating an error over. */
 
335
                }
 
336
                else {
 
337
                        memmove(p + insert_count, p,
 
338
                                buffer + strlen(buffer) - p + 1);
 
339
                        memcpy(p, chars_to_insert, insert_count);
 
340
                }
 
341
        }
 
342
}
 
343
 
 
344
/* Add the locale specific grouping characters to buffer.  Note
 
345
   that any decimal point (if it's present) in buffer is already
 
346
   locale-specific.  Return 0 on error, else 1. */
 
347
Py_LOCAL_INLINE(int)
 
348
add_thousands_grouping(char* buffer, size_t buf_size)
 
349
{
 
350
        Py_ssize_t len = strlen(buffer);
 
351
        struct lconv *locale_data = localeconv();
 
352
        const char *decimal_point = locale_data->decimal_point;
 
353
 
 
354
        /* Find the decimal point, if any.  We're only concerned
 
355
           about the characters to the left of the decimal when
 
356
           adding grouping. */
 
357
        char *p = strstr(buffer, decimal_point);
 
358
        if (!p) {
 
359
                /* No decimal, use the entire string. */
 
360
 
 
361
                /* If any exponent, adjust p. */
 
362
                p = strpbrk(buffer, "eE");
 
363
                if (!p)
 
364
                        /* No exponent and no decimal.  Use the entire
 
365
                           string. */
 
366
                        p = buffer + len;
 
367
        }
 
368
        /* At this point, p points just past the right-most character we
 
369
           want to format.  We need to add the grouping string for the
 
370
           characters between buffer and p. */
 
371
        return _PyBytes_InsertThousandsGrouping(buffer, len, p-buffer,
 
372
                                                buf_size, NULL, 1);
 
373
}
 
374
 
 
375
/* see FORMATBUFLEN in unicodeobject.c */
 
376
#define FLOAT_FORMATBUFLEN 120
 
377
 
 
378
/**
 
379
 * PyOS_ascii_formatd:
 
380
 * @buffer: A buffer to place the resulting string in
 
381
 * @buf_size: The length of the buffer.
 
382
 * @format: The printf()-style format to use for the
 
383
 *          code to use for converting. 
 
384
 * @d: The #gdouble to convert
 
385
 *
 
386
 * Converts a #gdouble to a string, using the '.' as
 
387
 * decimal point. To format the number you pass in
 
388
 * a printf()-style format string. Allowed conversion
 
389
 * specifiers are 'e', 'E', 'f', 'F', 'g', 'G', and 'n'.
 
390
 * 
 
391
 * 'n' is the same as 'g', except it uses the current locale.
 
392
 * 'Z' is the same as 'g', except it always has a decimal and
 
393
 *     at least one digit after the decimal.
 
394
 *
 
395
 * Return value: The pointer to the buffer with the converted string.
 
396
 **/
 
397
char *
 
398
PyOS_ascii_formatd(char       *buffer, 
 
399
                   size_t      buf_size, 
 
400
                   const char *format, 
 
401
                   double      d)
 
402
{
 
403
        char format_char;
 
404
        size_t format_len = strlen(format);
 
405
 
 
406
        /* For type 'n', we need to make a copy of the format string, because
 
407
           we're going to modify 'n' -> 'g', and format is const char*, so we
 
408
           can't modify it directly.  FLOAT_FORMATBUFLEN should be longer than
 
409
           we ever need this to be.  There's an upcoming check to ensure it's
 
410
           big enough. */
 
411
        /* Issue 2264: code 'Z' requires copying the format.  'Z' is 'g', but
 
412
           also with at least one character past the decimal. */
 
413
        char tmp_format[FLOAT_FORMATBUFLEN];
 
414
 
 
415
        /* The last character in the format string must be the format char */
 
416
        format_char = format[format_len - 1];
 
417
 
 
418
        if (format[0] != '%')
 
419
                return NULL;
 
420
 
 
421
        /* I'm not sure why this test is here.  It's ensuring that the format
 
422
           string after the first character doesn't have a single quote, a
 
423
           lowercase l, or a percent. This is the reverse of the commented-out
 
424
           test about 10 lines ago. */
 
425
        if (strpbrk(format + 1, "'l%"))
 
426
                return NULL;
 
427
 
 
428
        /* Also curious about this function is that it accepts format strings
 
429
           like "%xg", which are invalid for floats.  In general, the
 
430
           interface to this function is not very good, but changing it is
 
431
           difficult because it's a public API. */
 
432
 
 
433
        if (!(format_char == 'e' || format_char == 'E' || 
 
434
              format_char == 'f' || format_char == 'F' || 
 
435
              format_char == 'g' || format_char == 'G' ||
 
436
              format_char == 'n' || format_char == 'Z'))
 
437
                return NULL;
 
438
 
 
439
        /* Map 'n' or 'Z' format_char to 'g', by copying the format string and
 
440
           replacing the final char with a 'g' */
 
441
        if (format_char == 'n' || format_char == 'Z') {
 
442
                if (format_len + 1 >= sizeof(tmp_format)) {
 
443
                        /* The format won't fit in our copy.  Error out.  In
 
444
                           practice, this will never happen and will be
 
445
                           detected by returning NULL */
 
446
                        return NULL;
 
447
                }
 
448
                strcpy(tmp_format, format);
 
449
                tmp_format[format_len - 1] = 'g';
 
450
                format = tmp_format;
 
451
        }
 
452
 
 
453
 
 
454
        /* Have PyOS_snprintf do the hard work */
 
455
        PyOS_snprintf(buffer, buf_size, format, d);
 
456
 
 
457
        /* Do various fixups on the return string */
 
458
 
 
459
        /* Get the current locale, and find the decimal point string.
 
460
           Convert that string back to a dot.  Do not do this if using the
 
461
           'n' (number) format code, since we want to keep the localized
 
462
           decimal point in that case. */
 
463
        if (format_char != 'n')
 
464
                change_decimal_from_locale_to_dot(buffer);
 
465
 
 
466
        /* If an exponent exists, ensure that the exponent is at least
 
467
           MIN_EXPONENT_DIGITS digits, providing the buffer is large enough
 
468
           for the extra zeros.  Also, if there are more than
 
469
           MIN_EXPONENT_DIGITS, remove as many zeros as possible until we get
 
470
           back to MIN_EXPONENT_DIGITS */
 
471
        ensure_minumim_exponent_length(buffer, buf_size);
 
472
 
 
473
        /* If format_char is 'Z', make sure we have at least one character
 
474
           after the decimal point (and make sure we have a decimal point). */
 
475
        if (format_char == 'Z')
 
476
                ensure_decimal_point(buffer, buf_size);
 
477
 
 
478
        /* If format_char is 'n', add the thousands grouping. */
 
479
        if (format_char == 'n')
 
480
                if (!add_thousands_grouping(buffer, buf_size))
 
481
                        return NULL;
 
482
 
 
483
        return buffer;
 
484
}
 
485
 
 
486
double
 
487
PyOS_ascii_atof(const char *nptr)
 
488
{
 
489
        return PyOS_ascii_strtod(nptr, NULL);
 
490
}