]> granicus.if.org Git - python/commitdiff
Patch #1725 by Mark Dickinson, fixes incorrect conversion of -1e1000
authorGuido van Rossum <guido@python.org>
Sat, 5 Jan 2008 00:59:59 +0000 (00:59 +0000)
committerGuido van Rossum <guido@python.org>
Sat, 5 Jan 2008 00:59:59 +0000 (00:59 +0000)
and adds errors for -0x.

Lib/test/test_builtin.py
Lib/test/test_float.py
Python/pystrtod.c

index c5a1cc3fb214a33890d0c11551fb63878587b36f..255fb9696111d2177fefeaa14033b267b259ee33 100644 (file)
@@ -619,6 +619,11 @@ class BuiltinTest(unittest.TestCase):
         self.assertEqual(float("  3.14  "), 3.14)
         self.assertRaises(ValueError, float, "  0x3.1  ")
         self.assertRaises(ValueError, float, "  -0x3.p-1  ")
+        self.assertRaises(ValueError, float, "  +0x3.p-1  ")
+        self.assertRaises(ValueError, float, "++3.14")
+        self.assertRaises(ValueError, float, "+-3.14")
+        self.assertRaises(ValueError, float, "-+3.14")
+        self.assertRaises(ValueError, float, "--3.14")
         if have_unicode:
             self.assertEqual(float(unicode("  3.14  ")), 3.14)
             self.assertEqual(float(unicode("  \u0663.\u0661\u0664  ",'raw-unicode-escape')), 3.14)
@@ -648,6 +653,7 @@ class BuiltinTest(unittest.TestCase):
         self.assertRaises(ValueError, float, "  -3,14  ")
         self.assertRaises(ValueError, float, "  0x3.1  ")
         self.assertRaises(ValueError, float, "  -0x3.p-1  ")
+        self.assertRaises(ValueError, float, "  +0x3.p-1  ")
         self.assertEqual(float("  25.e-1  "), 2.5)
         self.assertEqual(fcmp(float("  .25e-1  "), .025), 0)
 
index 11f169054a9e1f139790314228fd8a62f72d546b..9d330403aa9df581be550f7d9b2aee835abf8917 100644 (file)
@@ -121,6 +121,13 @@ class IEEEFormatTestCase(unittest.TestCase):
             self.assertEquals(pos_pos(), neg_pos())
             self.assertEquals(pos_neg(), neg_neg())
 
+    if float.__getformat__("double").startswith("IEEE"):
+        def test_underflow_sign(self):
+            import math
+            # check that -1e-1000 gives -0.0, not 0.0
+            self.assertEquals(math.atan2(-1e-1000, -1), math.atan2(-0.0, -1))
+            self.assertEquals(math.atan2(float('-1e-1000'), -1),
+                              math.atan2(-0.0, -1))
 
 class ReprTestCase(unittest.TestCase):
     def test_repr(self):
index 6c19b45fd260070591307da151e8ab0639b7b8f8..14fb84ba715b8c5e9d59cfe7fc871316ce10e9d3 100644 (file)
@@ -48,6 +48,8 @@ PyOS_ascii_strtod(const char *nptr, char **endptr)
        size_t decimal_point_len;
        const char *p, *decimal_point_pos;
        const char *end = NULL; /* Silence gcc */
+       const char *digits_pos = NULL;
+       int negate = 0;
 
        assert(nptr != NULL);
 
@@ -60,18 +62,41 @@ PyOS_ascii_strtod(const char *nptr, char **endptr)
        assert(decimal_point_len != 0);
 
        decimal_point_pos = NULL;
+
+       /* We process any leading whitespace and the optional sign manually,
+          then pass the remainder to the system strtod.  This ensures that
+          the result of an underflow has the correct sign. (bug #1725)  */
+
+       p = nptr;
+       /* Skip leading space */
+       while (ISSPACE(*p))
+               p++;
+
+       /* Process leading sign, if present */
+       if (*p == '-') {
+               negate = 1;
+               p++;
+       } else if (*p == '+') {
+               p++;
+       }
+
+       /* What's left should begin with a digit, a decimal point, or one of
+          the letters i, I, n, N. It should not begin with 0x or 0X */
+       if ((!ISDIGIT(*p) &&
+            *p != '.' && *p != 'i' && *p != 'I' && *p != 'n' && *p != 'N')
+           ||
+           (*p == '0' && (p[1] == 'x' || p[1] == 'X')))
+       {
+               if (endptr)
+                       *endptr = (char*)nptr;
+               errno = EINVAL;
+               return val;
+       }
+       digits_pos = p;
+
        if (decimal_point[0] != '.' || 
            decimal_point[1] != 0)
        {
-               p = nptr;
-                 /* Skip leading space */
-               while (ISSPACE(*p))
-                       p++;
-
-                 /* Skip leading optional sign */
-               if (*p == '+' || *p == '-')
-                       p++;
-
                while (ISDIGIT(*p))
                        p++;
 
@@ -93,7 +118,8 @@ PyOS_ascii_strtod(const char *nptr, char **endptr)
                else if (strncmp(p, decimal_point, decimal_point_len) == 0)
                {
                        /* Python bug #1417699 */
-                       *endptr = (char*)nptr;
+                       if (endptr)
+                               *endptr = (char*)nptr;
                        errno = EINVAL;
                        return val;
                }
@@ -109,7 +135,8 @@ PyOS_ascii_strtod(const char *nptr, char **endptr)
                char *copy, *c;
 
                /* We need to convert the '.' to the locale specific decimal point */
-               copy = (char *)PyMem_MALLOC(end - nptr + 1 + decimal_point_len);
+               copy = (char *)PyMem_MALLOC(end - digits_pos +
+                                           1 + decimal_point_len);
                if (copy == NULL) {
                        if (endptr)
                                *endptr = (char *)nptr;
@@ -118,8 +145,8 @@ PyOS_ascii_strtod(const char *nptr, char **endptr)
                }
 
                c = copy;
-               memcpy(c, nptr, decimal_point_pos - nptr);
-               c += decimal_point_pos - nptr;
+               memcpy(c, digits_pos, decimal_point_pos - digits_pos);
+               c += decimal_point_pos - digits_pos;
                memcpy(c, decimal_point, decimal_point_len);
                c += decimal_point_len;
                memcpy(c, decimal_point_pos + 1, end - (decimal_point_pos + 1));
@@ -131,24 +158,27 @@ PyOS_ascii_strtod(const char *nptr, char **endptr)
                if (fail_pos)
                {
                        if (fail_pos > decimal_point_pos)
-                               fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1);
+                               fail_pos = (char *)digits_pos +
+                                       (fail_pos - copy) -
+                                       (decimal_point_len - 1);
                        else
-                               fail_pos = (char *)nptr + (fail_pos - copy);
+                               fail_pos = (char *)digits_pos +
+                                       (fail_pos - copy);
                }
 
                PyMem_FREE(copy);
 
        }
        else {
-               unsigned i = 0;
-               if (nptr[i] == '-')
-                       i++;
-               if (nptr[i] == '0' && (nptr[i+1] == 'x' || nptr[i+1] == 'X'))
-                       fail_pos = (char*)nptr;
-               else
-                       val = strtod(nptr, &fail_pos);
+               val = strtod(digits_pos, &fail_pos);
        }
 
+       if (fail_pos == digits_pos)
+               fail_pos = (char *)nptr;
+
+       if (negate && fail_pos != nptr)
+               val = -val;
+
        if (endptr)
                *endptr = fail_pos;