Index: Include/pystrtod.h =================================================================== --- Include/pystrtod.h (revision 72233) +++ Include/pystrtod.h (working copy) @@ -9,6 +9,9 @@ PyAPI_FUNC(double) PyOS_ascii_strtod(const char *str, char **ptr); PyAPI_FUNC(double) PyOS_ascii_atof(const char *str); PyAPI_FUNC(char *) PyOS_ascii_formatd(char *buffer, size_t buf_len, const char *format, double d); +PyAPI_FUNC(double) PyOS_string_to_double(const char *str, + char **endptr, + PyObject *overflow_exception); /* The caller is responsible for calling PyMem_Free to free the buffer that's is returned. */ Index: Python/dtoa.c =================================================================== --- Python/dtoa.c (revision 72233) +++ Python/dtoa.c (working copy) @@ -1355,6 +1355,7 @@ /* no break */ case 0: goto ret0; + /* modify original dtoa.c so that it doesn't accept leading whitespace case '\t': case '\n': case '\v': @@ -1362,6 +1363,7 @@ case '\r': case ' ': continue; + */ default: goto break2; } Index: Python/pystrtod.c =================================================================== --- Python/pystrtod.c (revision 72233) +++ Python/pystrtod.c (working copy) @@ -35,7 +35,7 @@ #ifndef PY_NO_SHORT_FLOAT_REPR double -PyOS_ascii_strtod(const char *nptr, char **endptr) +_PyOS_ascii_strtod(const char *nptr, char **endptr) { double result; _Py_SET_53BIT_PRECISION_HEADER; @@ -64,7 +64,7 @@ */ double -PyOS_ascii_strtod(const char *nptr, char **endptr) +_PyOS_ascii_strtod(const char *nptr, char **endptr) { char *fail_pos; double val = -1.0; @@ -92,15 +92,10 @@ and underflows */ errno = 0; - /* We process any leading whitespace and the optional sign manually, - then pass the remainder to the system strtod. This ensures that - the result of an underflow has the correct sign. (bug #1725) */ - + /* We process the optional sign manually, then pass the remainder to + the system strtod. This ensures that the result of an underflow + has the correct sign. (bug #1725) */ p = nptr; - /* Skip leading space */ - while (Py_ISSPACE(*p)) - p++; - /* Process leading sign, if present */ if (*p == '-') { negate = 1; @@ -185,8 +180,7 @@ copy = (char *)PyMem_MALLOC(end - digits_pos + 1 + decimal_point_len); if (copy == NULL) { - if (endptr) - *endptr = (char *)nptr; + *endptr = (char *)nptr; errno = ENOMEM; return val; } @@ -227,15 +221,12 @@ got_val: if (negate && fail_pos != nptr) val = -val; + *endptr = fail_pos; - if (endptr) - *endptr = fail_pos; - return val; invalid_string: - if (endptr) - *endptr = (char*)nptr; + *endptr = (char*)nptr; errno = EINVAL; return -1.0; } @@ -243,12 +234,100 @@ #endif double +PyOS_ascii_strtod(const char *nptr, char **endptr) +{ + char *fail_pos; + const char *p; + double x; + /* _PyOS_ascii_strtod already does everything that we want, + except that it doesn't parse leading whitespace */ + p = nptr; + while (Py_ISSPACE(*p)) + p++; + x = _PyOS_ascii_strtod(p, &fail_pos); + if (fail_pos == p) + fail_pos = (char *)nptr; + if (endptr) + *endptr = (char *)fail_pos; + return x; +} + +double PyOS_ascii_atof(const char *nptr) { return PyOS_ascii_strtod(nptr, NULL); } +/* PyOS_string_to_double is the recommended replacement for PyOS_ascii_strtod + and PyOS_ascii_atof. It converts a null-terminated byte string s + (interpreted as a string of ASCII characters) to a float. The string + should not have leading or trailing whitespace (in contrast, + PyOS_ascii_strtod allows leading whitespace but not trailing whitespace). + If endptr is NULL, try to convert the whole string. Raise ValueError and + return -1.0 if the string is not a valid representation of a floating-point + number. + + If endptr is non-NULL, try to convert as much of the string as possible. + If no initial segment of the string is the valid representation of a + floating-point number then *endptr is set to point to the beginning of the + string, -1.0 is returned and again ValueError is raised. + + On overflow (e.g., when trying to convert '1e500' on an IEEE 754 machine), + if overflow_exception is NULL then +-Py_HUGE_VAL is returned, and no Python + exception is raised. Otherwise, overflow_exception should point to a + a Python exception, this exception will be raised, -1.0 will be returned, + and *endptr will point just past the end of the converted value. + + If any other failure occurs (for example lack of memory), -1.0 is returned + and the appropriate Python exception will have been set. +*/ + +double +PyOS_string_to_double(const char *s, + char **endptr, + PyObject *overflow_exception) +{ + double x, result=-1.0; + char *fail_pos; + char buffer[256]; + + errno = 0; + PyFPE_START_PROTECT("PyOS_string_to_double", return -1.0) + x = _PyOS_ascii_strtod(s, &fail_pos); + PyFPE_END_PROTECT(x) + + if (errno == ENOMEM) { + PyErr_NoMemory(); + fail_pos = (char *)s; + } + else if (!endptr && (fail_pos == s || *fail_pos != '\0')) { + /* endptr is NULL and the string is not a valid float */ + PyOS_snprintf(buffer, sizeof(buffer), + "string cannot be converted to a float: " + "%.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + } + else if (fail_pos == s) { + PyOS_snprintf(buffer, sizeof(buffer), + "string cannot be converted to a float: " + "%.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + } + else if (errno == ERANGE && fabs(x) >= 1.0 && overflow_exception) { + PyOS_snprintf(buffer, sizeof(buffer), + "value too large to convert to float: " + "%.200s", s); + PyErr_SetString(overflow_exception, buffer); + } + else + result = x; + + if (endptr != NULL) + *endptr = fail_pos; + return result; +} + /* Given a string that may have a decimal point in the current locale, change it back to a dot. Since the string cannot get longer, no need for a maximum buffer size parameter. */