Index: Lib/decimal.py =================================================================== --- Lib/decimal.py (revision 79188) +++ Lib/decimal.py (working copy) @@ -845,8 +845,11 @@ # subject of what should happen for a comparison involving a NaN. # We take the following approach: # - # == comparisons involving a NaN always return False - # != comparisons involving a NaN always return True + # == comparisons involving a quiet NaN always return False + # != comparisons involving a quiet NaN always return True + # == or != comparisons involving a signaling NaN signal + # InvalidOperation, and return False or True as above if the + # InvalidOperation is not trapped. # <, >, <= and >= comparisons involving a (quiet or signaling) # NaN signal InvalidOperation, and return False if the # InvalidOperation is not trapped. @@ -854,19 +857,19 @@ # This behavior is designed to conform as closely as possible to # that specified by IEEE 754. - def __eq__(self, other): + def __eq__(self, other, context=None): other = _convert_other(other) if other is NotImplemented: return other - if self.is_nan() or other.is_nan(): + if self._check_nans(other, context): return False return self._cmp(other) == 0 - def __ne__(self, other): + def __ne__(self, other, context=None): other = _convert_other(other) if other is NotImplemented: return other - if self.is_nan() or other.is_nan(): + if self._check_nans(other, context): return True return self._cmp(other) != 0 Index: Lib/test/test_decimal.py =================================================================== --- Lib/test/test_decimal.py (revision 79188) +++ Lib/test/test_decimal.py (working copy) @@ -26,6 +26,7 @@ import math import os, sys +import operator import pickle, copy import unittest from decimal import * @@ -1064,19 +1065,57 @@ self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs def test_nan_comparisons(self): + # comparisons involving signaling nans signal InvalidOperation + + # order comparisons (<, <=, >, >=) involving only quiet nans + # also signal InvalidOperation + + # equality comparisons (==, !=) involving only quiet nans + # don't signal, but return False or True respectively. + n = Decimal('NaN') s = Decimal('sNaN') i = Decimal('Inf') f = Decimal('2') - for x, y in [(n, n), (n, i), (i, n), (n, f), (f, n), - (s, n), (n, s), (s, i), (i, s), (s, f), (f, s), (s, s)]: - self.assertTrue(x != y) - self.assertTrue(not (x == y)) - self.assertTrue(not (x < y)) - self.assertTrue(not (x <= y)) - self.assertTrue(not (x > y)) - self.assertTrue(not (x >= y)) + qnan_pairs = (n, n), (n, i), (i, n), (n, f), (f, n) + snan_pairs = (s, n), (n, s), (s, i), (i, s), (s, f), (f, s), (s, s) + order_ops = operator.lt, operator.le, operator.gt, operator.ge + equality_ops = operator.eq, operator.ne + + # results when InvalidOperation is not trapped + for x, y in qnan_pairs + snan_pairs: + for op in order_ops + equality_ops: + got = op(x, y) + expected = True if op is operator.ne else False + self.assertIs(expected, got, + "expected {0!r} for operator.{1}({2!r}, {3!r}); " + "got {4!r}".format( + expected, op.__name__, x, y, got)) + + # repeat the above, but this time trap the InvalidOperation + with localcontext() as ctx: + ctx.traps[InvalidOperation] = 1 + + for x, y in qnan_pairs: + for op in equality_ops: + got = op(x, y) + expected = True if op is operator.ne else False + self.assertIs(expected, got, + "expected {0!r} for " + "operator.{1}({2!r}, {3!r}); " + "got {4!r}".format( + expected, op.__name__, x, y, got)) + + for x, y in snan_pairs: + for op in equality_ops: + self.assertRaises(InvalidOperation, operator.eq, x, y) + self.assertRaises(InvalidOperation, operator.ne, x, y) + + for x, y in qnan_pairs + snan_pairs: + for op in order_ops: + self.assertRaises(InvalidOperation, op, x, y) + def test_copy_sign(self): d = Decimal(1).copy_sign(Decimal(-2))