[Python-checkins] r72810 - in python/branches/py3k: Include/pystrtod.h Lib/test/test_float.py Objects/floatobject.c Python/dtoa.c Python/pystrtod.c

mark.dickinson python-checkins at python.org
Thu May 21 00:05:26 CEST 2009


Author: mark.dickinson
Date: Thu May 21 00:05:25 2009
New Revision: 72810

Log:
Refactor to remove duplicated nan/inf parsing code in
pystrtod.c, floatobject.c and dtoa.c.


Modified:
   python/branches/py3k/Include/pystrtod.h
   python/branches/py3k/Lib/test/test_float.py
   python/branches/py3k/Objects/floatobject.c
   python/branches/py3k/Python/dtoa.c
   python/branches/py3k/Python/pystrtod.c

Modified: python/branches/py3k/Include/pystrtod.h
==============================================================================
--- python/branches/py3k/Include/pystrtod.h	(original)
+++ python/branches/py3k/Include/pystrtod.h	Thu May 21 00:05:25 2009
@@ -21,6 +21,8 @@
                                          int flags,
                                          int *type);
 
+PyAPI_FUNC(double) _Py_parse_inf_or_nan(const char *p, char **endptr);
+
 
 /* PyOS_double_to_string's "flags" parameter can be set to 0 or more of: */
 #define Py_DTSF_SIGN      0x01 /* always add the sign */

Modified: python/branches/py3k/Lib/test/test_float.py
==============================================================================
--- python/branches/py3k/Lib/test/test_float.py	(original)
+++ python/branches/py3k/Lib/test/test_float.py	Thu May 21 00:05:25 2009
@@ -532,6 +532,11 @@
         self.assertRaises(ValueError, float, "-INFI")
         self.assertRaises(ValueError, float, "infinitys")
 
+        self.assertRaises(ValueError, float, "++Inf")
+        self.assertRaises(ValueError, float, "-+inf")
+        self.assertRaises(ValueError, float, "+-infinity")
+        self.assertRaises(ValueError, float, "--Infinity")
+
     def test_inf_as_str(self):
         self.assertEqual(repr(1e300 * 1e300), "inf")
         self.assertEqual(repr(-1e300 * 1e300), "-inf")
@@ -563,6 +568,11 @@
         self.assertRaises(ValueError, float, "+na")
         self.assertRaises(ValueError, float, "-na")
 
+        self.assertRaises(ValueError, float, "++nan")
+        self.assertRaises(ValueError, float, "-+NAN")
+        self.assertRaises(ValueError, float, "+-NaN")
+        self.assertRaises(ValueError, float, "--nAn")
+
     def test_nan_as_str(self):
         self.assertEqual(repr(1e300 * 1e300 * 0), "nan")
         self.assertEqual(repr(-1e300 * 1e300 * 0), "nan")

Modified: python/branches/py3k/Objects/floatobject.c
==============================================================================
--- python/branches/py3k/Objects/floatobject.c	(original)
+++ python/branches/py3k/Objects/floatobject.c	Thu May 21 00:05:25 2009
@@ -1157,20 +1157,6 @@
 >>> 3.14159.hex()\n\
 '0x1.921f9f01b866ep+1'");
 
-/* Case-insensitive locale-independent string match used for nan and inf
-   detection. t should be lower-case and null-terminated.  Return a nonzero
-   result if the first strlen(t) characters of s match t and 0 otherwise. */
-
-static int
-case_insensitive_match(const char *s, const char *t)
-{
-	while(*t && Py_TOLOWER(*s) == *t) {
-		s++;
-		t++;
-	}
-	return *t ? 0 : 1;
-}
-
 /* Convert a hexadecimal string to a float. */
 
 static PyObject *
@@ -1180,7 +1166,7 @@
 	double x;
 	long exp, top_exp, lsb, key_digit;
 	char *s, *coeff_start, *s_store, *coeff_end, *exp_start, *s_end;
-	int half_eps, digit, round_up, sign=1;
+	int half_eps, digit, round_up, negate=0;
 	Py_ssize_t length, ndigits, fdigits, i;
 
 	/*
@@ -1237,33 +1223,24 @@
 	 * Parse the string *
 	 ********************/
 
-	/* leading whitespace and optional sign */
+	/* leading whitespace */
 	while (Py_ISSPACE(*s))
 		s++;
-	if (*s == '-') {
-		s++;
-		sign = -1;
-	}
-	else if (*s == '+')
-		s++;
 
 	/* infinities and nans */
-	if (*s == 'i' || *s == 'I') {
-		if (!case_insensitive_match(s+1, "nf"))
-			goto parse_error;
-		s += 3;
-		x = Py_HUGE_VAL;
-		if (case_insensitive_match(s, "inity"))
-			s += 5;
+	x = _Py_parse_inf_or_nan(s, &coeff_end);
+	if (coeff_end != s) {
+		s = coeff_end;
 		goto finished;
 	}
-	if (*s == 'n' || *s == 'N') {
-		if (!case_insensitive_match(s+1, "an"))
-			goto parse_error;
-		s += 3;
-		x = Py_NAN;
-		goto finished;
+
+	/* optional sign */
+	if (*s == '-') {
+		s++;
+		negate = 1;
 	}
+	else if (*s == '+')
+		s++;
 
 	/* [0x] */
 	s_store = s;
@@ -1400,7 +1377,7 @@
 		s++;
 	if (s != s_end)
 		goto parse_error;
-	result_as_float = Py_BuildValue("(d)", sign * x);
+	result_as_float = Py_BuildValue("(d)", negate ? -x : x);
 	if (result_as_float == NULL)
 		return NULL;
 	result = PyObject_CallObject(cls, result_as_float);

Modified: python/branches/py3k/Python/dtoa.c
==============================================================================
--- python/branches/py3k/Python/dtoa.c	(original)
+++ python/branches/py3k/Python/dtoa.c	Thu May 21 00:05:25 2009
@@ -264,15 +264,6 @@
 #define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))
 #define Big1 0xffffffff
 
-#ifndef NAN_WORD0
-#define NAN_WORD0 0x7ff80000
-#endif
-
-#ifndef NAN_WORD1
-#define NAN_WORD1 0
-#endif
-
-
 /* struct BCinfo is used to pass information from _Py_dg_strtod to bigcomp */
 
 typedef struct BCinfo BCinfo;
@@ -1026,25 +1017,6 @@
 #define Scale_Bit 0x10
 #define n_bigtens 5
 
-/* case insensitive string match, for recognising 'inf[inity]' and
-   'nan' strings. */
-
-static int
-match(const char **sp, char *t)
-{
-    int c, d;
-    const char *s = *sp;
-
-    while((d = *t++)) {
-        if ((c = *++s) >= 'A' && c <= 'Z')
-            c += 'a' - 'A';
-        if (c != d)
-            return 0;
-    }
-    *sp = s + 1;
-    return 1;
-}
-
 #define ULbits 32
 #define kshift 5
 #define kmask 31
@@ -1459,28 +1431,6 @@
     }
     if (!nd) {
         if (!nz && !nz0) {
-            /* Check for Nan and Infinity */
-            if (!bc.dplen)
-                switch(c) {
-                case 'i':
-                case 'I':
-                    if (match(&s,"nf")) {
-                        --s;
-                        if (!match(&s,"inity"))
-                            ++s;
-                        word0(&rv) = 0x7ff00000;
-                        word1(&rv) = 0;
-                        goto ret;
-                    }
-                    break;
-                case 'n':
-                case 'N':
-                    if (match(&s, "an")) {
-                        word0(&rv) = NAN_WORD0;
-                        word1(&rv) = NAN_WORD1;
-                        goto ret;
-                    }
-                }
           ret0:
             s = s00;
             sign = 0;

Modified: python/branches/py3k/Python/pystrtod.c
==============================================================================
--- python/branches/py3k/Python/pystrtod.c	(original)
+++ python/branches/py3k/Python/pystrtod.c	Thu May 21 00:05:25 2009
@@ -3,6 +3,57 @@
 #include <Python.h>
 #include <locale.h>
 
+/* _Py_parse_inf_or_nan: Attempt to parse a string of the form "nan", "inf" or
+   "infinity", with an optional leading sign of "+" or "-".  On success,
+   return the NaN or Infinity as a double and set *endptr to point just beyond
+   the successfully parsed portion of the string.  On failure, return -1.0 and
+   set *endptr to point to the start of the string. */
+
+static int
+case_insensitive_match(const char *s, const char *t)
+{
+	while(*t && Py_TOLOWER(*s) == *t) {
+		s++;
+		t++;
+	}
+	return *t ? 0 : 1;
+}
+
+double
+_Py_parse_inf_or_nan(const char *p, char **endptr)
+{
+	double retval;
+	const char *s;
+	int negate = 0;
+
+	s = p;
+	if (*s == '-') {
+		negate = 1;
+		s++;
+	}
+	else if (*s == '+') {
+		s++;
+	}
+	if (case_insensitive_match(s, "inf")) {
+		s += 3;
+		if (case_insensitive_match(s, "inity"))
+			s += 5;
+		retval = negate ? -Py_HUGE_VAL : Py_HUGE_VAL;
+	}
+#ifdef Py_NAN
+	else if (case_insensitive_match(s, "nan")) {
+		s += 3;
+		retval = negate ? -Py_NAN : Py_NAN;
+	}
+#endif
+	else {
+		s = p;
+		retval = -1.0;
+	}
+	*endptr = (char *)s;
+	return retval;
+}
+
 /**
  * PyOS_ascii_strtod:
  * @nptr:    the string to convert to a numeric value.
@@ -49,6 +100,10 @@
 	result = _Py_dg_strtod(nptr, endptr);
 	_Py_SET_53BIT_PRECISION_END;
 
+	if (*endptr == nptr)
+		/* string might represent and inf or nan */
+		result = _Py_parse_inf_or_nan(nptr, endptr);
+
 	return result;
 
 }
@@ -63,19 +118,6 @@
    correctly rounded results.
 */
 
-/* Case-insensitive string match used for nan and inf detection; t should be
-   lower-case.  Returns 1 for a successful match, 0 otherwise. */
-
-static int
-case_insensitive_match(const char *s, const char *t)
-{
-	while(*t && Py_TOLOWER(*s) == *t) {
-		s++;
-		t++;
-	}
-	return *t ? 0 : 1;
-}
-
 double
 _PyOS_ascii_strtod(const char *nptr, char **endptr)
 {
@@ -101,6 +143,11 @@
 
 	decimal_point_pos = NULL;
 
+	/* Parse infinities and nans */
+	val = _Py_parse_inf_or_nan(nptr, endptr);
+	if (*endptr != nptr)
+		return val;
+
 	/* Set errno to zero, so that we can distinguish zero results
 	   and underflows */
 	errno = 0;
@@ -118,31 +165,6 @@
 		p++;
 	}
 
-	/* Parse infinities and nans */
-	if (*p == 'i' || *p == 'I') {
-		if (case_insensitive_match(p+1, "nf")) {
-			val = Py_HUGE_VAL;
-			if (case_insensitive_match(p+3, "inity"))
-				fail_pos = (char *)p+8;
-			else
-				fail_pos = (char *)p+3;
-			goto got_val;
-		}
-		else
-			goto invalid_string;
-	}
-#ifdef Py_NAN
-	if (*p == 'n' || *p == 'N') {
-		if (case_insensitive_match(p+1, "an")) {
-			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'))
@@ -231,7 +253,6 @@
 	if (fail_pos == digits_pos)
 		goto invalid_string;
 
-  got_val:
 	if (negate && fail_pos != nptr)
 		val = -val;
 	*endptr = fail_pos;


More information about the Python-checkins mailing list