]> granicus.if.org Git - python/commitdiff
Issue #5981: Fix some float.fromhex bugs related to inf and nan handling.
authorMark Dickinson <dickinsm@gmail.com>
Mon, 11 May 2009 15:33:08 +0000 (15:33 +0000)
committerMark Dickinson <dickinsm@gmail.com>
Mon, 11 May 2009 15:33:08 +0000 (15:33 +0000)
Lib/test/test_float.py
Misc/NEWS
Objects/floatobject.c

index 1c6c4124c7debf9f7e47ff183160034c02ff57c7..345a81510690310ab452d6d4d0bcc934d1f04081 100644 (file)
@@ -462,6 +462,11 @@ class HexFloatTestCase(unittest.TestCase):
             'snan',
             'NaNs',
             'nna',
+            'an',
+            'nf',
+            'nfinity',
+            'inity',
+            'iinity',
             '0xnan',
             '',
             ' ',
@@ -510,6 +515,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;
index f560bd8c74e61dc0448fd78bcb83cd180fd0a4a1..c79ee75529513041eeaaf1a89eab5a41d98968c3 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,12 @@ What's New in Python 2.7 alpha 1
 Core and Builtins
 -----------------
 
+- Issue #5981: Fix three minor inf/nan issues in float.fromhex:
+  (1) inf and nan strings with trailing whitespace were incorrectly
+  rejected;  (2) parsing of strings representing infinities and nans
+  was locale aware; and (3) the interpretation of fromhex('-nan')
+  didn't match that of float('-nan').
+
 - Issue #5920: For float.__format__, change the behavior with the
   empty presentation type (that is, not one of 'e', 'f', 'g', or 'n')
   to be like 'g' but with at least one decimal point and with a
index 6b9a310dd337e388511f94fe246161cf0e918f14..e11d758805f674ad95d2c28a246978139e54fc04 100644 (file)
@@ -1159,6 +1159,20 @@ Return a hexadecimal representation of a floating-point number.\n\
 >>> 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 *
@@ -1235,13 +1249,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;
        }
 
@@ -1294,12 +1315,6 @@ float_fromhex(PyObject *cls, PyObject *arg)
        else
                exp = 0;
 
-       /* optional trailing whitespace leading to the end of the string */
-       while (Py_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) :                    \
@@ -1313,7 +1328,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)
@@ -1329,7 +1344,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)
@@ -1344,7 +1359,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
@@ -1378,10 +1393,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 (Py_ISSPACE(*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);