]> granicus.if.org Git - python/commitdiff
Issue #14521: Make result of float('nan') and float('-nan') more consistent across...
authorMark Dickinson <mdickinson@enthought.com>
Sun, 29 Apr 2012 14:31:56 +0000 (15:31 +0100)
committerMark Dickinson <mdickinson@enthought.com>
Sun, 29 Apr 2012 14:31:56 +0000 (15:31 +0100)
Include/dtoa.h
Lib/test/test_float.py
Misc/NEWS
Python/dtoa.c
Python/pystrtod.c

index 819bd0f207069c3fbb0a56f41638997de1de8b67..9bfb6251db831c59579b157625e8a3fc492d1e77 100644 (file)
@@ -8,6 +8,8 @@ PyAPI_FUNC(double) _Py_dg_strtod(const char *str, char **ptr);
 PyAPI_FUNC(char *) _Py_dg_dtoa(double d, int mode, int ndigits,
                         int *decpt, int *sign, char **rve);
 PyAPI_FUNC(void) _Py_dg_freedtoa(char *s);
+PyAPI_FUNC(double) _Py_dg_stdnan(int sign);
+PyAPI_FUNC(double) _Py_dg_infinity(int sign);
 
 
 #ifdef __cplusplus
index dc0c2912be22be9aa61651316f0be02d68b0d3ae..3cee3836a35cedcbacbce8a6fc75318cf9c7def3 100644 (file)
@@ -870,6 +870,19 @@ class InfNanTest(unittest.TestCase):
         self.assertFalse(NAN.is_inf())
         self.assertFalse((0.).is_inf())
 
+    def test_inf_signs(self):
+        self.assertEqual(copysign(1.0, float('inf')), 1.0)
+        self.assertEqual(copysign(1.0, float('-inf')), -1.0)
+
+    @unittest.skipUnless(getattr(sys, 'float_repr_style', '') == 'short',
+                         "applies only when using short float repr style")
+    def test_nan_signs(self):
+        # When using the dtoa.c code, the sign of float('nan') should
+        # be predictable.
+        self.assertEqual(copysign(1.0, float('nan')), 1.0)
+        self.assertEqual(copysign(1.0, float('-nan')), -1.0)
+
+
 fromHex = float.fromhex
 toHex = float.hex
 class HexFloatTestCase(unittest.TestCase):
index 83f2e3ea04e883cc5f9f59f3b121946744ff7867..fbc8b5f2537abb978717e929754b9765a79c8a4c 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.3.0 Alpha 3?
 Core and Builtins
 -----------------
 
+- Issue #14521: Make result of float('nan') and float('-nan') more
+  consistent across platforms.
+
 - Issue #14646: __import__() sets __loader__ if the loader did not.
 
 - Issue #14605: No longer have implicit entries in sys.meta_path. If
index 82b6faa80c2e733a34e3f74c08de104160777721..83861ace1801b53ef177cf7b0a223a6299fc1836 100644 (file)
@@ -265,6 +265,16 @@ typedef union { double d; ULong L[2]; } U;
 #define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))
 #define Big1 0xffffffff
 
+/* Standard NaN used by _Py_dg_stdnan. */
+
+#define NAN_WORD0 0x7ff80000
+#define NAN_WORD1 0
+
+/* Bits of the representation of positive infinity. */
+
+#define POSINF_WORD0 0x7ff00000
+#define POSINF_WORD1 0
+
 /* struct BCinfo is used to pass information from _Py_dg_strtod to bigcomp */
 
 typedef struct BCinfo BCinfo;
@@ -1486,6 +1496,36 @@ bigcomp(U *rv, const char *s0, BCinfo *bc)
     return 0;
 }
 
+/* Return a 'standard' NaN value.
+
+   There are exactly two quiet NaNs that don't arise by 'quieting' signaling
+   NaNs (see IEEE 754-2008, section 6.2.1).  If sign == 0, return the one whose
+   sign bit is cleared.  Otherwise, return the one whose sign bit is set.
+*/
+
+double
+_Py_dg_stdnan(int sign)
+{
+    U rv;
+    word0(&rv) = NAN_WORD0;
+    word1(&rv) = NAN_WORD1;
+    if (sign)
+        word0(&rv) |= Sign_bit;
+    return dval(&rv);
+}
+
+/* Return positive or negative infinity, according to the given sign (0 for
+ * positive infinity, 1 for negative infinity). */
+
+double
+_Py_dg_infinity(int sign)
+{
+    U rv;
+    word0(&rv) = POSINF_WORD0;
+    word1(&rv) = POSINF_WORD1;
+    return sign ? -dval(&rv) : dval(&rv);
+}
+
 double
 _Py_dg_strtod(const char *s00, char **se)
 {
@@ -1886,20 +1926,20 @@ _Py_dg_strtod(const char *s00, char **se)
         bd2++;
 
         /* At this stage bd5 - bb5 == e == bd2 - bb2 + bbe, bb2 - bs2 == 1,
-          and bs == 1, so:
+           and bs == 1, so:
 
               tdv == bd * 10**e = bd * 2**(bbe - bb2 + bd2) * 5**(bd5 - bb5)
               srv == bb * 2**bbe = bb * 2**(bbe - bb2 + bb2)
-             0.5 ulp(srv) == 2**(bbe-1) = bs * 2**(bbe - bb2 + bs2)
+              0.5 ulp(srv) == 2**(bbe-1) = bs * 2**(bbe - bb2 + bs2)
 
-          It follows that:
+           It follows that:
 
               M * tdv = bd * 2**bd2 * 5**bd5
               M * srv = bb * 2**bb2 * 5**bb5
               M * 0.5 ulp(srv) = bs * 2**bs2 * 5**bb5
 
-          for some constant M.  (Actually, M == 2**(bb2 - bbe) * 5**bb5, but
-          this fact is not needed below.)
+           for some constant M.  (Actually, M == 2**(bb2 - bbe) * 5**bb5, but
+           this fact is not needed below.)
         */
 
         /* Remove factor of 2**i, where i = min(bb2, bd2, bs2). */
index 7bf21c0bc1a9ec3c466733469c98a3127a14e4a1..4ab8f08d22beb562de75db8a7e6393933936b365 100644 (file)
@@ -22,6 +22,43 @@ case_insensitive_match(const char *s, const char *t)
    the successfully parsed portion of the string.  On failure, return -1.0 and
    set *endptr to point to the start of the string. */
 
+#ifndef PY_NO_SHORT_FLOAT_REPR
+
+double
+_Py_parse_inf_or_nan(const char *p, char **endptr)
+{
+    double retval;
+    const char *s;
+    int negate = 0;
+
+    s = p;
+    if (*s == '-') {
+        negate = 1;
+        s++;
+    }
+    else if (*s == '+') {
+        s++;
+    }
+    if (case_insensitive_match(s, "inf")) {
+        s += 3;
+        if (case_insensitive_match(s, "inity"))
+            s += 5;
+        retval = _Py_dg_infinity(negate);
+    }
+    else if (case_insensitive_match(s, "nan")) {
+        s += 3;
+        retval = _Py_dg_stdnan(negate);
+    }
+    else {
+        s = p;
+        retval = -1.0;
+    }
+    *endptr = (char *)s;
+    return retval;
+}
+
+#else
+
 double
 _Py_parse_inf_or_nan(const char *p, char **endptr)
 {
@@ -57,6 +94,8 @@ _Py_parse_inf_or_nan(const char *p, char **endptr)
     return retval;
 }
 
+#endif
+
 /**
  * _PyOS_ascii_strtod:
  * @nptr:    the string to convert to a numeric value.