New submission from Christian Heimes:

The patch unifies the creation and representation of "inf", "-inf" and
"nan" on all platforms. 

>>> float("inf")
inf
>>> float("-inf")
-inf
>>> float("nan")
nan
>>> repr(1e300 * 1e300)
'inf'
>>> repr(1e300 * 1e300 * 0)
'nan'
>>> repr(1e300 * 1e300 * -1)
'-inf'

----------
assignee: tiran
files: trunk_float_inf_nan.patch
keywords: patch, py3k
messages: 58661
nosy: tiran
priority: normal
severity: normal
status: open
title: Float patch for inf and nan on Windows (and other platforms)
versions: Python 2.6, Python 3.0
Added file: http://bugs.python.org/file8961/trunk_float_inf_nan.patch

__________________________________
Tracker <[EMAIL PROTECTED]>
<http://bugs.python.org/issue1635>
__________________________________
Index: Include/pyport.h
===================================================================
--- Include/pyport.h	(revision 59504)
+++ Include/pyport.h	(working copy)
@@ -393,6 +393,15 @@
 #define Py_HUGE_VAL HUGE_VAL
 #endif
 
+/* Py_NAN
+ * Some value that evaluates to a NaN. On IEEE 754 platforms INF*0 or
+ * INF/INF works. Define Py_NO_NAN in pyconfig.h if your platform
+ * doesn't support NaNs.
+ */
+#if !defined(Py_NAN) && !defined(Py_NO_NAN)
+#define Py_NAN Py_HUGE_VAL * 0
+#endif
+
 /* Py_OVERFLOWED(X)
  * Return 1 iff a libm function overflowed.  Set errno to 0 before calling
  * a libm function, and invoke this macro after, passing the function
Index: Lib/test/test_float.py
===================================================================
--- Lib/test/test_float.py	(revision 59504)
+++ Lib/test/test_float.py	(working copy)
@@ -3,6 +3,12 @@
 import os
 from test import test_support
 
+def isinf(x):
+    return x * 0.5 == x
+    
+def isnan(x):
+    return x != x
+
 class FormatFunctionsTestCase(unittest.TestCase):
 
     def setUp(self):
@@ -128,13 +134,78 @@
             self.assertEqual(v, eval(repr(v)))
         floats_file.close()
 
+# Beginning with Python 2.6 float has cross platform compatible
+# ways to create and representate inf and nan
+class InfNanTest(unittest.TestCase):
+    def test_inf_from_str(self):
+        self.assert_(isinf(float("inf")))
+        self.assert_(isinf(float("+inf")))
+        self.assert_(isinf(float("-inf")))
 
+        self.assertEqual(repr(float("inf")), "inf")
+        self.assertEqual(repr(float("+inf")), "inf")
+        self.assertEqual(repr(float("-inf")), "-inf")
+
+        self.assertEqual(repr(float("INF")), "inf")
+        self.assertEqual(repr(float("+Inf")), "inf")
+        self.assertEqual(repr(float("-iNF")), "-inf")
+        
+        self.assertEqual(str(float("inf")), "inf")
+        self.assertEqual(str(float("+inf")), "inf")
+        self.assertEqual(str(float("-inf")), "-inf")
+
+        self.assertRaises(ValueError, float, "info")
+        self.assertRaises(ValueError, float, "+info")
+        self.assertRaises(ValueError, float, "-info")
+        self.assertRaises(ValueError, float, "in")
+        self.assertRaises(ValueError, float, "+in")
+        self.assertRaises(ValueError, float, "-in")
+        
+    def test_inf_as_str(self):
+        self.assertEqual(repr(1e300 * 1e300), "inf")
+        self.assertEqual(repr(-1e300 * 1e300), "-inf")
+        
+        self.assertEqual(str(1e300 * 1e300), "inf")
+        self.assertEqual(str(-1e300 * 1e300), "-inf")
+
+    def test_nan_from_str(self):
+        self.assert_(isnan(float("nan")))
+        self.assert_(isnan(float("+nan")))
+        self.assert_(isnan(float("-nan")))
+
+        self.assertEqual(repr(float("nan")), "nan")
+        self.assertEqual(repr(float("+nan")), "nan")
+        self.assertEqual(repr(float("-nan")), "nan")
+
+        self.assertEqual(repr(float("NAN")), "nan")
+        self.assertEqual(repr(float("+NAn")), "nan")
+        self.assertEqual(repr(float("-NaN")), "nan")
+        
+        self.assertEqual(str(float("nan")), "nan")
+        self.assertEqual(str(float("+nan")), "nan")
+        self.assertEqual(str(float("-nan")), "nan")
+
+        self.assertRaises(ValueError, float, "nana")
+        self.assertRaises(ValueError, float, "+nana")
+        self.assertRaises(ValueError, float, "-nana")
+        self.assertRaises(ValueError, float, "na")
+        self.assertRaises(ValueError, float, "+na")
+        self.assertRaises(ValueError, float, "-na")
+
+    def test_nan_as_str(self):
+        self.assertEqual(repr(1e300 * 1e300 * 0), "nan")
+        self.assertEqual(repr(-1e300 * 1e300 * 0), "nan")
+        
+        self.assertEqual(str(1e300 * 1e300 * 0), "nan")
+        self.assertEqual(str(-1e300 * 1e300 * 0), "nan")
+
 def test_main():
     test_support.run_unittest(
         FormatFunctionsTestCase,
         UnknownFormatTestCase,
         IEEEFormatTestCase,
-        #ReprTestCase
+        ReprTestCase,
+        InfNanTest,
         )
 
 if __name__ == '__main__':
Index: Objects/floatobject.c
===================================================================
--- Objects/floatobject.c	(revision 59504)
+++ Objects/floatobject.c	(working copy)
@@ -128,7 +128,7 @@
 PyObject *
 PyFloat_FromString(PyObject *v, char **pend)
 {
-	const char *s, *last, *end;
+	const char *s, *last, *end, *sp;
 	double x;
 	char buffer[256]; /* for errors */
 #ifdef Py_USING_UNICODE
@@ -171,6 +171,7 @@
 		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
@@ -186,7 +187,35 @@
 	   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 pp[6], *p = pp;
+		int i, sign = 1;
+
+		for(i = 0; *sp && i<5; i++) {
+			if (isalpha(*sp))
+				*p++ = tolower(Py_CHARMASK(*sp++));
+			else
+				*p++ = *sp++;
+		}
+		*p = '\0';
+		p = pp;
+
+		if (*p == '-') {
+			sign = -1;
+			p++;
+		}
+		if (*p == '+') {
+			p++;
+		}
+		if (strncmp(p, "inf", 4) == 0) {
+			return PyFloat_FromDouble(sign * HUGE_VAL);
+		}
+#ifdef Py_NAN
+		if(strncmp(p, "nan", 4) == 0) {
+			return PyFloat_FromDouble(Py_NAN);
+		}
+#endif
 		PyOS_snprintf(buffer, sizeof(buffer),
 			      "invalid literal for float(): %.200s", s);
 		PyErr_SetString(PyExc_ValueError, buffer);
@@ -271,6 +300,8 @@
 {
 	register char *cp;
 	char format[32];
+	int i;
+
 	/* Subroutine for float_repr and float_print.
 	   We want float numbers to be recognizable as such,
 	   i.e., they should contain a decimal point or an exponent.
@@ -293,7 +324,39 @@
 		*cp++ = '.';
 		*cp++ = '0';
 		*cp++ = '\0';
+		return;
 	}
+	/* checking the next three items should be more than enough to 
+	 * detect inf or nan, even on Windows. We check for inf or nan
+	 * at last because they are rare cases.
+	 */
+	for (i=0; *cp != '\0' && i<3; cp++, i++) {
+		if (isdigit(Py_CHARMASK(*cp)) || *cp == '.')
+			continue;
+		/* found something that is neither a digit nor point
+		 * it might be a NaN or INF
+		 */
+#ifdef Py_NAN
+		if (Py_IS_NAN(v->ob_fval)) {
+			cp = buf;
+			*cp++ = 'n';
+			*cp++ = 'a';
+			*cp++ = 'n';
+			*cp++ = '\0';
+		}
+#endif
+		if (Py_IS_INFINITY(v->ob_fval)) {
+			cp = buf;
+			if (*cp == '-')
+				cp++;
+			*cp++ = 'i';
+			*cp++ = 'n';
+			*cp++ = 'f';
+			*cp++ = '\0';
+		}
+		break;
+	}
+
 }
 
 /* XXX PyFloat_AsStringEx should not be a public API function (for one
_______________________________________________
Python-bugs-list mailing list 
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to