mirror of
https://github.com/python/cpython.git
synced 2025-07-19 09:15:34 +00:00
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.
This commit is contained in:
parent
75930f85df
commit
725bfd8489
10 changed files with 253 additions and 96 deletions
16
Python/ast.c
16
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
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)
|
||||
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.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");
|
||||
break;
|
||||
}
|
||||
buf[n] = '\0';
|
||||
c.imag = PyOS_string_to_double(buf, NULL, NULL);
|
||||
if (c.imag == -1.0 && PyErr_Occurred())
|
||||
break;
|
||||
retval = PyComplex_FromCComplex(c);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue