From ce05717daa31be8e2bb93e405f8f37fc8d0fe233 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 11 May 2009 16:09:39 +0000 Subject: Merged revisions 72564 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72564 | mark.dickinson | 2009-05-11 16:33:08 +0100 (Mon, 11 May 2009) | 2 lines Issue #5981: Fix some float.fromhex bugs related to inf and nan handling. ........ --- Lib/test/test_float.py | 31 +++++++++++++++++++++++++++++ Misc/NEWS | 5 +++++ Objects/floatobject.c | 54 ++++++++++++++++++++++++++++++++++---------------- 3 files changed, 73 insertions(+), 17 deletions(-) diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 0b1ae96..23c0c54 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -391,6 +391,11 @@ class HexFloatTestCase(unittest.TestCase): 'snan', 'NaNs', 'nna', + 'an', + 'nf', + 'nfinity', + 'inity', + 'iinity', '0xnan', '', ' ', @@ -439,6 +444,32 @@ class HexFloatTestCase(unittest.TestCase): 'got %r instead' % (x, result)) + def test_whitespace(self): + value_pairs = [ + ('inf', INF), + ('-Infinity', -INF), + ('nan', NAN), + ('1.0', 1.0), + ('-0x.2', -0.125), + ('-0.0', -0.0) + ] + whitespace = [ + '', + ' ', + '\t', + '\n', + '\n \t', + '\f', + '\v', + '\r' + ] + for inp, expected in value_pairs: + for lead in whitespace: + for trail in whitespace: + got = fromHex(lead + inp + trail) + self.identical(got, expected) + + def test_from_hex(self): MIN = self.MIN; MAX = self.MAX; diff --git a/Misc/NEWS b/Misc/NEWS index a068c5e..43e6024 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,11 @@ What's New in Python 2.6.3 Core and Builtins ----------------- +- Issue #5981: Fix two minor inf/nan issues in float.fromhex: (1) inf + and nan strings with trailing whitespace were incorrectly rejected + and (2) the interpretation of fromhex('-nan') didn't match that of + float('-nan'). + - Issue #5890: in subclasses of 'property' the __doc__ attribute was shadowed by classtype's, even if it was None. property now inserts the __doc__ into the subclass instance __dict__. diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 4f041f4..2d00c16 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -1263,6 +1263,20 @@ Return a hexadecimal representation of a floating-point number.\n\ >>> 3.14159.hex()\n\ '0x1.921f9f01b866ep+1'"); +/* Case-insensitive 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 && tolower(*s) == *t) { + s++; + t++; + } + return *t ? 0 : 1; +} + /* Convert a hexadecimal string to a float. */ static PyObject * @@ -1329,7 +1343,7 @@ float_fromhex(PyObject *cls, PyObject *arg) ********************/ /* leading whitespace and optional sign */ - while (isspace(*s)) + while (*s && isspace(Py_CHARMASK(*s))) s++; if (*s == '-') { s++; @@ -1339,13 +1353,20 @@ float_fromhex(PyObject *cls, PyObject *arg) s++; /* infinities and nans */ - if (PyOS_strnicmp(s, "nan", 4) == 0) { - x = Py_NAN; + 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; goto finished; } - if (PyOS_strnicmp(s, "inf", 4) == 0 || - PyOS_strnicmp(s, "infinity", 9) == 0) { - x = sign*Py_HUGE_VAL; + if (*s == 'n' || *s == 'N') { + if (!case_insensitive_match(s+1, "an")) + goto parse_error; + s += 3; + x = Py_NAN; goto finished; } @@ -1398,12 +1419,6 @@ float_fromhex(PyObject *cls, PyObject *arg) else exp = 0; - /* optional trailing whitespace leading to the end of the string */ - while (isspace(*s)) - s++; - if (s != s_end) - goto parse_error; - /* for 0 <= j < ndigits, HEX_DIGIT(j) gives the jth most significant digit */ #define HEX_DIGIT(j) hex_from_char(*((j) < fdigits ? \ coeff_end-(j) : \ @@ -1417,7 +1432,7 @@ float_fromhex(PyObject *cls, PyObject *arg) while (ndigits > 0 && HEX_DIGIT(ndigits-1) == 0) ndigits--; if (ndigits == 0 || exp < LONG_MIN/2) { - x = sign * 0.0; + x = 0.0; goto finished; } if (exp > LONG_MAX/2) @@ -1433,7 +1448,7 @@ float_fromhex(PyObject *cls, PyObject *arg) /* catch almost all nonextreme cases of overflow and underflow here */ if (top_exp < DBL_MIN_EXP - DBL_MANT_DIG) { - x = sign * 0.0; + x = 0.0; goto finished; } if (top_exp > DBL_MAX_EXP) @@ -1448,7 +1463,7 @@ float_fromhex(PyObject *cls, PyObject *arg) /* no rounding required */ for (i = ndigits-1; i >= 0; i--) x = 16.0*x + HEX_DIGIT(i); - x = sign * ldexp(x, (int)(exp)); + x = ldexp(x, (int)(exp)); goto finished; } /* rounding required. key_digit is the index of the hex digit @@ -1482,10 +1497,15 @@ float_fromhex(PyObject *cls, PyObject *arg) goto overflow_error; } } - x = sign * ldexp(x, (int)(exp+4*key_digit)); + x = ldexp(x, (int)(exp+4*key_digit)); finished: - result_as_float = Py_BuildValue("(d)", x); + /* optional trailing whitespace leading to the end of the string */ + while (*s && isspace(Py_CHARMASK(*s))) + s++; + if (s != s_end) + goto parse_error; + result_as_float = Py_BuildValue("(d)", sign * x); if (result_as_float == NULL) return NULL; result = PyObject_CallObject(cls, result_as_float); -- cgit v0.12