summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Dickinson <mdickinson@enthought.com>2012-04-29 14:31:56 (GMT)
committerMark Dickinson <mdickinson@enthought.com>2012-04-29 14:31:56 (GMT)
commite383e82e0484aed79f2c78516e3f223345408d4b (patch)
tree665c6b5274695e846f6b66fa88d673eccd0bc402
parentd68ac85e9afd3d7e5dfc8fe2e2853d3371cc08d2 (diff)
downloadcpython-e383e82e0484aed79f2c78516e3f223345408d4b.zip
cpython-e383e82e0484aed79f2c78516e3f223345408d4b.tar.gz
cpython-e383e82e0484aed79f2c78516e3f223345408d4b.tar.bz2
Issue #14521: Make result of float('nan') and float('-nan') more consistent across platforms. Further, don't rely on Py_HUGE_VAL for float('inf').
-rw-r--r--Include/dtoa.h2
-rw-r--r--Lib/test/test_float.py13
-rw-r--r--Misc/NEWS3
-rw-r--r--Python/dtoa.c50
-rw-r--r--Python/pystrtod.c39
5 files changed, 102 insertions, 5 deletions
diff --git a/Include/dtoa.h b/Include/dtoa.h
index 819bd0f..9bfb625 100644
--- a/Include/dtoa.h
+++ b/Include/dtoa.h
@@ -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
diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py
index dc0c291..3cee383 100644
--- a/Lib/test/test_float.py
+++ b/Lib/test/test_float.py
@@ -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):
diff --git a/Misc/NEWS b/Misc/NEWS
index 83f2e3e..fbc8b5f 100644
--- 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
diff --git a/Python/dtoa.c b/Python/dtoa.c
index 82b6faa..83861ac 100644
--- a/Python/dtoa.c
+++ b/Python/dtoa.c
@@ -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). */
diff --git a/Python/pystrtod.c b/Python/pystrtod.c
index 7bf21c0..4ab8f08 100644
--- a/Python/pystrtod.c
+++ b/Python/pystrtod.c
@@ -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.