]> granicus.if.org Git - python/commitdiff
Backport r71967 changes from py3k to trunk.
authorMark Dickinson <dickinsm@gmail.com>
Sun, 26 Apr 2009 16:04:05 +0000 (16:04 +0000)
committerMark Dickinson <dickinsm@gmail.com>
Sun, 26 Apr 2009 16:04:05 +0000 (16:04 +0000)
(Internal plumbing changes for float parsing.)

Objects/floatobject.c
Python/pystrtod.c

index 382b991bdfb419804fef4e102960df2eb65fcf41..a433697fa8f622ce32843a55220c1b4fd0656cea 100644 (file)
@@ -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);
 }
 
index f9a8831b11ef92785ad86f02aedf086eea99c730..68161644fb5add58178dc6cd1f617d2c10401072 100644 (file)
@@ -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