Kaydet (Commit) 9b5e2314 authored tarafından Facundo Batista's avatar Facundo Batista

The constructor from tuple was way too permissive: it allowed bad

coefficient numbers, floats in the sign, and other details that
generated directly the wrong number in the best case, or triggered
misfunctionality in the alorithms.

Test cases added for these issues. Thanks Mark Dickinson.
üst 91ac4224
...@@ -562,20 +562,46 @@ class Decimal(object): ...@@ -562,20 +562,46 @@ class Decimal(object):
# tuple/list conversion (possibly from as_tuple()) # tuple/list conversion (possibly from as_tuple())
if isinstance(value, (list,tuple)): if isinstance(value, (list,tuple)):
if len(value) != 3: if len(value) != 3:
raise ValueError('Invalid arguments') raise ValueError('Invalid tuple size in creation of Decimal '
if value[0] not in (0,1): 'from list or tuple. The list or tuple '
raise ValueError('Invalid sign') 'should have exactly three elements.')
for digit in value[1]: # process sign. The isinstance test rejects floats
if not isinstance(digit, (int,long)) or digit < 0: if not (isinstance(value[0], (int, long)) and value[0] in (0,1)):
raise ValueError("The second value in the tuple must be " raise ValueError("Invalid sign. The first value in the tuple "
"composed of non negative integer elements.") "should be an integer; either 0 for a "
"positive number or 1 for a negative number.")
self._sign = value[0] self._sign = value[0]
self._int = tuple(value[1]) if value[2] == 'F':
if value[2] in ('F','n','N'): # infinity: value[1] is ignored
self._int = (0,)
self._exp = value[2] self._exp = value[2]
self._is_special = True self._is_special = True
else: else:
self._exp = int(value[2]) # process and validate the digits in value[1]
digits = []
for digit in value[1]:
if isinstance(digit, (int, long)) and 0 <= digit <= 9:
# skip leading zeros
if digits or digit != 0:
digits.append(digit)
else:
raise ValueError("The second value in the tuple must "
"be composed of integers in the range "
"0 through 9.")
if value[2] in ('n', 'N'):
# NaN: digits form the diagnostic
self._int = tuple(digits)
self._exp = value[2]
self._is_special = True
elif isinstance(value[2], (int, long)):
# finite number: digits give the coefficient
self._int = tuple(digits or [0])
self._exp = value[2]
self._is_special = False
else:
raise ValueError("The third value in the tuple must "
"be an integer, or one of the "
"strings 'F', 'n', 'N'.")
return self return self
if isinstance(value, float): if isinstance(value, float):
......
...@@ -452,13 +452,18 @@ class DecimalExplicitConstructionTest(unittest.TestCase): ...@@ -452,13 +452,18 @@ class DecimalExplicitConstructionTest(unittest.TestCase):
#bad sign #bad sign
self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) ) self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) )
self.assertRaises(ValueError, Decimal, (0., (4, 3, 4, 9, 1), 2) )
self.assertRaises(ValueError, Decimal, (Decimal(1), (4, 3, 4, 9, 1), 2))
#bad exp #bad exp
self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') ) self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') )
self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 0.) )
self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), '1') )
#bad coefficients #bad coefficients
self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) ) self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) ) self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
self.assertRaises(ValueError, Decimal, (1, (4, 10, 4, 9, 1), 2) )
def test_explicit_from_Decimal(self): def test_explicit_from_Decimal(self):
...@@ -1060,6 +1065,28 @@ class DecimalUsabilityTest(unittest.TestCase): ...@@ -1060,6 +1065,28 @@ class DecimalUsabilityTest(unittest.TestCase):
d = Decimal("Infinity") d = Decimal("Infinity")
self.assertEqual(d.as_tuple(), (0, (0,), 'F') ) self.assertEqual(d.as_tuple(), (0, (0,), 'F') )
#leading zeros in coefficient should be stripped
d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), -2) )
self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), -2) )
d = Decimal( (1, (0, 0, 0), 37) )
self.assertEqual(d.as_tuple(), (1, (0,), 37))
d = Decimal( (1, (), 37) )
self.assertEqual(d.as_tuple(), (1, (0,), 37))
#leading zeros in NaN diagnostic info should be stripped
d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), 'n') )
self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), 'n') )
d = Decimal( (1, (0, 0, 0), 'N') )
self.assertEqual(d.as_tuple(), (1, (), 'N') )
d = Decimal( (1, (), 'n') )
self.assertEqual(d.as_tuple(), (1, (), 'n') )
#coefficient in infinity should be ignored
d = Decimal( (0, (4, 5, 3, 4), 'F') )
self.assertEqual(d.as_tuple(), (0, (0,), 'F'))
d = Decimal( (1, (0, 2, 7, 1), 'F') )
self.assertEqual(d.as_tuple(), (1, (0,), 'F'))
def test_immutability_operations(self): def test_immutability_operations(self):
# Do operations and check that it didn't change change internal objects. # Do operations and check that it didn't change change internal objects.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment