diff options
author | Mark Dickinson <dickinsm@gmail.com> | 2009-05-03 20:33:40 (GMT) |
---|---|---|
committer | Mark Dickinson <dickinsm@gmail.com> | 2009-05-03 20:33:40 (GMT) |
commit | 725bfd8489e444aedd8dfd686a27ffc308657155 (patch) | |
tree | 99d4e0cc2953794a67a5cff491e1897723a119ff /Python | |
parent | 75930f85df76472686a8c4eb587a2a70562f61fe (diff) | |
download | cpython-725bfd8489e444aedd8dfd686a27ffc308657155.zip cpython-725bfd8489e444aedd8dfd686a27ffc308657155.tar.gz cpython-725bfd8489e444aedd8dfd686a27ffc308657155.tar.bz2 |
Issue #5914: Add new C-API function PyOS_string_to_double, to complement
PyOS_double_to_string, and deprecate PyOS_ascii_strtod and PyOS_ascii_atof.
Diffstat (limited to 'Python')
-rw-r--r-- | Python/ast.c | 16 | ||||
-rw-r--r-- | Python/dtoa.c | 5 | ||||
-rw-r--r-- | Python/marshal.c | 25 | ||||
-rw-r--r-- | Python/pystrtod.c | 117 |
4 files changed, 124 insertions, 39 deletions
diff --git a/Python/ast.c b/Python/ast.c index b08cf9b..1c79359 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -3162,18 +3162,18 @@ parsenumber(struct compiling *c, const char *s) #ifndef WITHOUT_COMPLEX if (imflag) { compl.real = 0.; - PyFPE_START_PROTECT("atof", return 0) - compl.imag = PyOS_ascii_atof(s); - PyFPE_END_PROTECT(c) - return PyComplex_FromCComplex(compl); + compl.imag = PyOS_string_to_double(s, (char **)&end, NULL); + if (compl.imag == -1.0 && PyErr_Occurred()) + return NULL; + return PyComplex_FromCComplex(compl); } else #endif { - PyFPE_START_PROTECT("atof", return 0) - dx = PyOS_ascii_atof(s); - PyFPE_END_PROTECT(dx) - return PyFloat_FromDouble(dx); + dx = PyOS_string_to_double(s, NULL, NULL); + if (dx == -1.0 && PyErr_Occurred()) + return NULL; + return PyFloat_FromDouble(dx); } } diff --git a/Python/dtoa.c b/Python/dtoa.c index 1d96304..82434bc 100644 --- a/Python/dtoa.c +++ b/Python/dtoa.c @@ -61,6 +61,9 @@ * that hasn't been MALLOC'ed, private_mem should only be used when k <= * Kmax. * + * 7. _Py_dg_strtod has been modified so that it doesn't accept strings with + * leading whitespace. + * ***************************************************************/ /* Please send bug reports for the original dtoa.c code to David M. Gay (dmg @@ -1355,6 +1358,7 @@ _Py_dg_strtod(const char *s00, char **se) /* 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 +1366,7 @@ _Py_dg_strtod(const char *s00, char **se) case '\r': case ' ': continue; + */ default: goto break2; } diff --git a/Python/marshal.c b/Python/marshal.c index 4ad873e..4e9c129 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -670,18 +670,17 @@ r_object(RFILE *p) { char buf[256]; double dx; + retval = NULL; n = r_byte(p); if (n == EOF || r_string(buf, (int)n, p) != n) { PyErr_SetString(PyExc_EOFError, "EOF read where object expected"); - retval = NULL; break; } buf[n] = '\0'; - retval = NULL; - PyFPE_START_PROTECT("atof", break) - dx = PyOS_ascii_atof(buf); - PyFPE_END_PROTECT(dx) + dx = PyOS_string_to_double(buf, NULL, NULL); + if (dx == -1.0 && PyErr_Occurred()) + break; retval = PyFloat_FromDouble(dx); break; } @@ -710,29 +709,27 @@ r_object(RFILE *p) { char buf[256]; Py_complex c; + retval = NULL; n = r_byte(p); if (n == EOF || r_string(buf, (int)n, p) != n) { PyErr_SetString(PyExc_EOFError, "EOF read where object expected"); - retval = NULL; break; } buf[n] = '\0'; - retval = NULL; - PyFPE_START_PROTECT("atof", break;) - c.real = PyOS_ascii_atof(buf); - PyFPE_END_PROTECT(c) + c.real = PyOS_string_to_double(buf, NULL, NULL); + if (c.real == -1.0 && PyErr_Occurred()) + break; n = r_byte(p); if (n == EOF || r_string(buf, (int)n, p) != n) { PyErr_SetString(PyExc_EOFError, "EOF read where object expected"); - retval = NULL; break; } buf[n] = '\0'; - PyFPE_START_PROTECT("atof", break) - c.imag = PyOS_ascii_atof(buf); - PyFPE_END_PROTECT(c) + c.imag = PyOS_string_to_double(buf, NULL, NULL); + if (c.imag == -1.0 && PyErr_Occurred()) + break; retval = PyComplex_FromCComplex(c); break; } diff --git a/Python/pystrtod.c b/Python/pystrtod.c index 1040610..66242d8 100644 --- a/Python/pystrtod.c +++ b/Python/pystrtod.c @@ -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 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) */ 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 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) 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 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) 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,27 +221,116 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) got_val: if (negate && fail_pos != nptr) val = -val; - - if (endptr) - *endptr = fail_pos; + *endptr = fail_pos; return val; invalid_string: - if (endptr) - *endptr = (char*)nptr; + *endptr = (char*)nptr; errno = EINVAL; return -1.0; } #endif +/* PyOS_ascii_strtod is DEPRECATED in Python 3.1 */ + +double +PyOS_ascii_strtod(const char *nptr, char **endptr) +{ + char *fail_pos; + const char *p; + double x; + + if (PyErr_WarnEx(PyExc_DeprecationWarning, + "PyOS_ascii_strtod and PyOS_ascii_atof are " + "deprecated. Use PyOS_string_to_double " + "instead.", 1) < 0) + return -1.0; + + /* _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; +} + +/* PyOS_ascii_strtod is DEPRECATED in Python 3.1 */ + double PyOS_ascii_atof(const char *nptr) { return PyOS_ascii_strtod(nptr, NULL); } +/* PyOS_string_to_double is the recommended replacement for the deprecated + PyOS_ascii_strtod and PyOS_ascii_atof functions. 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). The conversion is independent of the current locale. + + 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; + + 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')) + PyErr_Format(PyExc_ValueError, + "could not convert string to float: " + "%.200s", s); + else if (fail_pos == s) + PyErr_Format(PyExc_ValueError, + "could not convert string to float: " + "%.200s", s); + else if (errno == ERANGE && fabs(x) >= 1.0 && overflow_exception) + PyErr_Format(overflow_exception, + "value too large to convert to float: " + "%.200s", s); + 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 |