diff --git a/Lib/test/ieee754.txt b/Lib/test/ieee754.txt --- a/Lib/test/ieee754.txt +++ b/Lib/test/ieee754.txt @@ -67,10 +67,16 @@ NaNs are never equal to another number, even itself >>> NAN == NAN False + +NaNs are unorderable >>> NAN < 0 -False +Traceback (most recent call last): + .. +ValueError: NaN is unorderable >>> NAN >= 0 -False +Traceback (most recent call last): + .. +ValueError: NaN is unorderable All operations involving a NaN return a NaN except for nan**0 and 1**nan. >>> 1 + NAN diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -160,6 +160,15 @@ self.assertRaises(OverflowError, float('inf').as_integer_ratio) self.assertRaises(OverflowError, float('-inf').as_integer_ratio) self.assertRaises(ValueError, float('nan').as_integer_ratio) + # NaNs are unorderable + self.assertRaises(ValueError, lambda: NAN < 0) + self.assertRaises(ValueError, lambda: NAN > 0) + self.assertRaises(ValueError, lambda: NAN <= 0) + self.assertRaises(ValueError, lambda: NAN >= 0) + self.assertRaises(ValueError, lambda: 0 < NAN) + self.assertRaises(ValueError, lambda: 0 > NAN) + self.assertRaises(ValueError, lambda: 0 <= NAN) + self.assertRaises(ValueError, lambda: 0 >= NAN) def test_float_containment(self): floats = (INF, -INF, 0.0, 1.0, NAN) diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py --- a/Lib/test/test_fractions.py +++ b/Lib/test/test_fractions.py @@ -464,10 +464,10 @@ self.assertFalse(float('inf') < F(1, 2)) self.assertTrue(float('-inf') < F(0, 10)) - self.assertFalse(float('nan') < F(-3, 7)) + self.assertRaises(ValueError, lambda: float('nan') < F(-3, 7)) self.assertTrue(F(1, 2) < float('inf')) self.assertFalse(F(17, 12) < float('-inf')) - self.assertFalse(F(144, -89) < float('nan')) + self.assertRaises(lambda: F(144, -89) < float('nan')) def testMixedLessEqual(self): self.assertTrue(0.5 <= F(1, 2)) @@ -481,10 +481,10 @@ self.assertFalse(float('inf') <= F(1, 2)) self.assertTrue(float('-inf') <= F(0, 10)) - self.assertFalse(float('nan') <= F(-3, 7)) + self.assertRaises(lambda: float('nan') <= F(-3, 7)) self.assertTrue(F(1, 2) <= float('inf')) self.assertFalse(F(17, 12) <= float('-inf')) - self.assertFalse(F(144, -89) <= float('nan')) + self.assertRaises(lambda: F(144, -89) <= float('nan')) def testBigFloatComparisons(self): # Because 10**23 can't be represented exactly as a float: diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -174,10 +174,28 @@ flags ) +def is_negative_zero(x): + return x == 0 and math.copysign(1, x) < 0 + +def almost_equal(value, expected): + if math.isfinite(expected) and math.isfinite(value): + if is_negative_zero(expected): + return is_negative_zero(value) + if is_negative_zero(value): + return is_negative_zero(expected) + return abs(value-expected) <= eps + if math.isnan(expected): + return math.isnan(value) + return value == expected + class MathTests(unittest.TestCase): + + def test_xxx(self): + self.assertTrue(is_negative_zero(-0.0)) + self.assertFalse(almost_equal(0.0, -0.0)) def ftest(self, name, value, expected): - if abs(value-expected) > eps: + if not almost_equal(value, expected): # Use %r instead of %f so the error message # displays full precision. Otherwise discrepancies # in the last few bits will lead to very confusing diff --git a/Objects/floatobject.c b/Objects/floatobject.c --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -492,6 +492,12 @@ goto Unimplemented; Compare: + if (op != Py_EQ && op != Py_NE) + if (Py_IS_NAN(i) || Py_IS_NAN(j)) { + PyErr_SetString(PyExc_ValueError, + "NaN is unorderable"); + return NULL; + } PyFPE_START_PROTECT("richcompare", return NULL) switch (op) { case Py_EQ: