[Python-checkins] r71969 - in python/trunk: Objects/floatobject.c Python/pystrtod.c

mark.dickinson python-checkins at python.org
Sun Apr 26 18:04:06 CEST 2009


Author: mark.dickinson
Date: Sun Apr 26 18:04:05 2009
New Revision: 71969

Log:
Backport r71967 changes from py3k to trunk.
(Internal plumbing changes for float parsing.)


Modified:
   python/trunk/Objects/floatobject.c
   python/trunk/Python/pystrtod.c

Modified: python/trunk/Objects/floatobject.c
==============================================================================
--- python/trunk/Objects/floatobject.c	(original)
+++ python/trunk/Objects/floatobject.c	Sun Apr 26 18:04:05 2009
@@ -177,7 +177,7 @@
 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 @@
 				"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);
 }
 

Modified: python/trunk/Python/pystrtod.c
==============================================================================
--- python/trunk/Python/pystrtod.c	(original)
+++ python/trunk/Python/pystrtod.c	Sun Apr 26 18:04:05 2009
@@ -71,6 +71,10 @@
 
 	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 @@
 	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 @@
 		{
 			decimal_point_pos = p++;
 
+			/* locate end of number */
 			while (ISDIGIT(*p))
 				p++;
 
@@ -124,27 +155,16 @@
 			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 @@
 	}
 
 	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 @@
 		*endptr = fail_pos;
 
 	return val;
+
+  invalid_string:
+	if (endptr)
+		*endptr = (char*)nptr;
+	errno = EINVAL;
+	return -1.0;
 }
 
 double


More information about the Python-checkins mailing list