Issue #7279: Make comparisons involving a Decimal sNaN signal InvalidOperation.
This commit is contained in:
parent
ea2d389474
commit
e096e82e82
|
@ -845,8 +845,11 @@ class Decimal(object):
|
||||||
# subject of what should happen for a comparison involving a NaN.
|
# subject of what should happen for a comparison involving a NaN.
|
||||||
# We take the following approach:
|
# We take the following approach:
|
||||||
#
|
#
|
||||||
# == comparisons involving a NaN always return False
|
# == comparisons involving a quiet NaN always return False
|
||||||
# != comparisons involving a NaN always return True
|
# != 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)
|
# <, >, <= and >= comparisons involving a (quiet or signaling)
|
||||||
# NaN signal InvalidOperation, and return False if the
|
# NaN signal InvalidOperation, and return False if the
|
||||||
# InvalidOperation is not trapped.
|
# InvalidOperation is not trapped.
|
||||||
|
@ -854,19 +857,19 @@ class Decimal(object):
|
||||||
# This behavior is designed to conform as closely as possible to
|
# This behavior is designed to conform as closely as possible to
|
||||||
# that specified by IEEE 754.
|
# that specified by IEEE 754.
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other, context=None):
|
||||||
other = _convert_other(other, allow_float=True)
|
other = _convert_other(other, allow_float=True)
|
||||||
if other is NotImplemented:
|
if other is NotImplemented:
|
||||||
return other
|
return other
|
||||||
if self.is_nan() or other.is_nan():
|
if self._check_nans(other, context):
|
||||||
return False
|
return False
|
||||||
return self._cmp(other) == 0
|
return self._cmp(other) == 0
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other, context=None):
|
||||||
other = _convert_other(other, allow_float=True)
|
other = _convert_other(other, allow_float=True)
|
||||||
if other is NotImplemented:
|
if other is NotImplemented:
|
||||||
return other
|
return other
|
||||||
if self.is_nan() or other.is_nan():
|
if self._check_nans(other, context):
|
||||||
return True
|
return True
|
||||||
return self._cmp(other) != 0
|
return self._cmp(other) != 0
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ with the corresponding argument.
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import os, sys
|
import os, sys
|
||||||
|
import operator
|
||||||
import pickle, copy
|
import pickle, copy
|
||||||
import unittest
|
import unittest
|
||||||
from decimal import *
|
from decimal import *
|
||||||
|
@ -1080,18 +1081,56 @@ class DecimalArithmeticOperatorsTest(unittest.TestCase):
|
||||||
self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs
|
self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs
|
||||||
|
|
||||||
def test_nan_comparisons(self):
|
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')
|
n = Decimal('NaN')
|
||||||
s = Decimal('sNaN')
|
s = Decimal('sNaN')
|
||||||
i = Decimal('Inf')
|
i = Decimal('Inf')
|
||||||
f = Decimal('2')
|
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)]:
|
qnan_pairs = (n, n), (n, i), (i, n), (n, f), (f, n)
|
||||||
self.assertTrue(x != y)
|
snan_pairs = (s, n), (n, s), (s, i), (i, s), (s, f), (f, s), (s, s)
|
||||||
self.assertTrue(not (x == y))
|
order_ops = operator.lt, operator.le, operator.gt, operator.ge
|
||||||
self.assertTrue(not (x < y))
|
equality_ops = operator.eq, operator.ne
|
||||||
self.assertTrue(not (x <= y))
|
|
||||||
self.assertTrue(not (x > y))
|
# results when InvalidOperation is not trapped
|
||||||
self.assertTrue(not (x >= y))
|
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):
|
def test_copy_sign(self):
|
||||||
d = Decimal(1).copy_sign(Decimal(-2))
|
d = Decimal(1).copy_sign(Decimal(-2))
|
||||||
|
|
|
@ -35,6 +35,10 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #7279: Comparisons involving a Decimal signaling NaN now
|
||||||
|
signal InvalidOperation instead of returning False. (Comparisons
|
||||||
|
involving a quiet NaN are unchanged.)
|
||||||
|
|
||||||
- Issue #2531: Comparison operations between floats and Decimal
|
- Issue #2531: Comparison operations between floats and Decimal
|
||||||
instances now return a result based on the numeric values of the
|
instances now return a result based on the numeric values of the
|
||||||
operands; previously they returned an arbitrary result based on
|
operands; previously they returned an arbitrary result based on
|
||||||
|
|
Loading…
Reference in New Issue