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