From 31b217c12586b884db7965deac4ca005e17c2f5c Mon Sep 17 00:00:00 2001 From: "mark.dickinson" Date: Sun, 26 Apr 2009 16:04:05 +0000 Subject: [PATCH] Backport r71967 changes from py3k to trunk. (Internal plumbing changes for float parsing.) git-svn-id: http://svn.python.org/projects/python/trunk@71969 6015fed2-1504-0410-9fe1-9d1591cc4771 --- Objects/floatobject.c | 81 +++++++++++++---------------------------------- Python/pystrtod.c | 87 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 78 insertions(+), 90 deletions(-) diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 382b991bdf..a433697fa8 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -177,7 +177,7 @@ still supported but now *officially* useless: if pend is not NULL, PyObject * PyFloat_FromString(PyObject *v, char **pend) { - const char *s, *last, *end, *sp; + const char *s, *last, *end; double x; char buffer[256]; /* for errors */ #ifdef Py_USING_UNICODE @@ -212,81 +212,42 @@ PyFloat_FromString(PyObject *v, char **pend) "float() argument must be a string or a number"); return NULL; } - last = s + len; + while (*s && isspace(Py_CHARMASK(*s))) s++; - if (*s == '\0') { - PyErr_SetString(PyExc_ValueError, "empty string for float()"); - return NULL; - } - sp = s; - /* We don't care about overflow or underflow. If the platform supports - * them, infinities and signed zeroes (on underflow) are fine. - * However, strtod can return 0 for denormalized numbers, where atof - * does not. So (alas!) we special-case a zero result. Note that - * whether strtod sets errno on underflow is not defined, so we can't - * key off errno. - */ + /* We don't care about overflow or underflow. If the platform + * supports them, infinities and signed zeroes (on underflow) are + * fine. */ + errno = 0; PyFPE_START_PROTECT("strtod", return NULL) x = PyOS_ascii_strtod(s, (char **)&end); PyFPE_END_PROTECT(x) - errno = 0; - /* Believe it or not, Solaris 2.6 can move end *beyond* the null - byte at the end of the string, when the input is inf(inity). */ - if (end > last) - end = last; - /* Check for inf and nan. This is done late because it rarely happens. */ if (end == s) { - char *p = (char*)sp; - int sign = 1; - - if (*p == '-') { - sign = -1; - p++; - } - if (*p == '+') { - p++; - } - if (PyOS_strnicmp(p, "inf", 4) == 0) { - Py_RETURN_INF(sign); - } - if (PyOS_strnicmp(p, "infinity", 9) == 0) { - Py_RETURN_INF(sign); - } -#ifdef Py_NAN - if(PyOS_strnicmp(p, "nan", 4) == 0) { - Py_RETURN_NAN; + if (errno == ENOMEM) + PyErr_NoMemory(); + else { + PyOS_snprintf(buffer, sizeof(buffer), + "invalid literal for float(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); } -#endif - PyOS_snprintf(buffer, sizeof(buffer), - "invalid literal for float(): %.200s", s); - PyErr_SetString(PyExc_ValueError, buffer); return NULL; } /* Since end != s, the platform made *some* kind of sense out of the input. Trust it. */ while (*end && isspace(Py_CHARMASK(*end))) end++; - if (*end != '\0') { - PyOS_snprintf(buffer, sizeof(buffer), - "invalid literal for float(): %.200s", s); - PyErr_SetString(PyExc_ValueError, buffer); - return NULL; - } - else if (end != last) { - PyErr_SetString(PyExc_ValueError, - "null byte in argument for float()"); + if (end != last) { + if (*end == '\0') + PyErr_SetString(PyExc_ValueError, + "null byte in argument for float()"); + else { + PyOS_snprintf(buffer, sizeof(buffer), + "invalid literal for float(): %.200s", s); + PyErr_SetString(PyExc_ValueError, buffer); + } return NULL; } - if (x == 0.0) { - /* See above -- may have been strtod being anal - about denorms. */ - PyFPE_START_PROTECT("atof", return NULL) - x = PyOS_ascii_atof(s); - PyFPE_END_PROTECT(x) - errno = 0; /* whether atof ever set errno is undefined */ - } return PyFloat_FromDouble(x); } diff --git a/Python/pystrtod.c b/Python/pystrtod.c index f9a8831b11..68161644fb 100644 --- a/Python/pystrtod.c +++ b/Python/pystrtod.c @@ -71,6 +71,10 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) decimal_point_pos = NULL; + /* Set errno to zero, so that we can distinguish zero results + 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) */ @@ -84,27 +88,53 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) if (*p == '-') { negate = 1; p++; - } else if (*p == '+') { + } + else if (*p == '+') { p++; } - /* What's left should begin with a digit, a decimal point, or one of - the letters i, I, n, N. It should not begin with 0x or 0X */ - if ((!ISDIGIT(*p) && - *p != '.' && *p != 'i' && *p != 'I' && *p != 'n' && *p != 'N') - || - (*p == '0' && (p[1] == 'x' || p[1] == 'X'))) - { - if (endptr) - *endptr = (char*)nptr; - errno = EINVAL; - return val; + /* Parse infinities and nans */ + if (*p == 'i' || *p == 'I') { + if (PyOS_strnicmp(p, "inf", 3) == 0) { + val = Py_HUGE_VAL; + if (PyOS_strnicmp(p+3, "inity", 5) == 0) + fail_pos = (char *)p+8; + else + fail_pos = (char *)p+3; + goto got_val; + } + else + goto invalid_string; } - digits_pos = p; +#ifdef Py_NAN + if (*p == 'n' || *p == 'N') { + if (PyOS_strnicmp(p, "nan", 3) == 0) { + val = Py_NAN; + fail_pos = (char *)p+3; + goto got_val; + } + else + goto invalid_string; + } +#endif + + /* Some platform strtods accept hex floats; Python shouldn't (at the + moment), so we check explicitly for strings starting with '0x'. */ + if (*p == '0' && (*(p+1) == 'x' || *(p+1) == 'X')) + goto invalid_string; + + /* Check that what's left begins with a digit or decimal point */ + if (!ISDIGIT(*p) && *p != '.') + goto invalid_string; - if (decimal_point[0] != '.' || + digits_pos = p; + if (decimal_point[0] != '.' || decimal_point[1] != 0) { + /* Look for a '.' in the input; if present, it'll need to be + swapped for the current locale's decimal point before we + call strtod. On the other hand, if we find the current + locale's decimal point then the input is invalid. */ while (ISDIGIT(*p)) p++; @@ -112,6 +142,7 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) { decimal_point_pos = p++; + /* locate end of number */ while (ISDIGIT(*p)) p++; @@ -124,27 +155,16 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) end = p; } else if (strncmp(p, decimal_point, decimal_point_len) == 0) - { /* Python bug #1417699 */ - if (endptr) - *endptr = (char*)nptr; - errno = EINVAL; - return val; - } + goto invalid_string; /* For the other cases, we need not convert the decimal point */ } - /* Set errno to zero, so that we can distinguish zero results - and underflows */ - errno = 0; - - if (decimal_point_pos) - { + if (decimal_point_pos) { char *copy, *c; - - /* We need to convert the '.' to the locale specific decimal - point */ + /* Create a copy of the input, with the '.' converted to the + locale-specific decimal point */ copy = (char *)PyMem_MALLOC(end - digits_pos + 1 + decimal_point_len); if (copy == NULL) { @@ -185,8 +205,9 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) } if (fail_pos == digits_pos) - fail_pos = (char *)nptr; + goto invalid_string; + got_val: if (negate && fail_pos != nptr) val = -val; @@ -194,6 +215,12 @@ PyOS_ascii_strtod(const char *nptr, char **endptr) *endptr = fail_pos; return val; + + invalid_string: + if (endptr) + *endptr = (char*)nptr; + errno = EINVAL; + return -1.0; } double -- 2.11.4.GIT