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