features/cdecimal: b00f8fa70126 Lib/test/decimal_tests.py (original) (raw)
Merge from default.
line wrap: on
line source
Copyright (c) 2004 Python Software Foundation.
All rights reserved.
Written by Eric Price
and Facundo Batista
and Raymond Hettinger
and Aahz (aahz at pobox.com)
and Tim Peters
""" These are the test cases for the Decimal and Cdecimal modules. There are two groups of tests, Arithmetic and Behaviour. The former test the Decimal arithmetic using the tests provided by Mike Cowlishaw. The latter test the pythonic behaviour according to PEP 327. Cowlishaw's tests can be downloaded from: www2.hursley.ibm.com/decimal/dectest.zip This test module can be called from command line with one parameter (Arithmetic or Behaviour) to test each part, or without parameter to test both parts. If you're working through IDLE, you can import this test module and call test_main() with the corresponding argument. """ import math import os, sys import operator import pickle, copy import unittest import numbers from test.support import run_unittest, run_doctest, is_resource_enabled import random try: import threading except ImportError: threading = None if sys.modules['have_cdecimal']: from cdecimal import * HAVE_CDECIMAL = True HAVE_CONFIG_64 = (MAX_EMAX == 999999999999999999) else: from decimal import * HAVE_CDECIMAL = False HAVE_CONFIG_64 = False if HAVE_CDECIMAL and (not HAVE_CONFIG_64): readcontext = Context() readcontext.unsafe_setprec(1070000000) readcontext.unsafe_setemax(1070000000) readcontext.unsafe_setemin(-1070000000) readcontext._traps = 0 def read_unlimited(v, context): savetraps = context._traps saveflags = context._flags context._traps = 0 vv = Decimal(v, context) context._traps = savetraps if context._flags & DecInvalidOperation: # Do it the hard way context._flags = saveflags vv = readcontext.create_decimal(v) return vv else: def read_unlimited(v, context): return Decimal(v, context) if HAVE_CDECIMAL: def is_integer(v): return v.is_integer() def is_special(v): return v.is_special() def raise_error(context, flag): context.flags[flag] = True if context.traps[flag]: raise flag def make_grouping(lst): return ''.join([chr(x) for x in lst]) def get_fmt(x, override=None, fmt='n'): return Decimal.format(Decimal(x), fmt, override) else: def is_integer(v): return v._isinteger() def is_special(v): return v._is_special def raise_error(context, flag): context._raise_error(flag) def make_grouping(lst): return lst def get_fmt(x, locale, fmt='n'): return Decimal.format(Decimal(x), fmt, _localeconv=locale)
Useful Test Constant
Signals = tuple(getcontext().flags.keys())
Signals ordered with respect to precedence: when an operation
produces multiple signals, signals occurring later in the list
should be handled before those occurring earlier in the list.
OrderedSignals = (Clamped, Rounded, Inexact, Subnormal, Underflow, Overflow, DivisionByZero, InvalidOperation)
Tests are built around these assumed context defaults.
test_main() restores the original context.
def init(): global ORIGINAL_CONTEXT ORIGINAL_CONTEXT = getcontext().copy() DefaultTestContext = Context( prec = 9, rounding = ROUND_HALF_EVEN, traps = dict.fromkeys(Signals, 0) ) setcontext(DefaultTestContext)
decorator for skipping tests on non-IEEE 754 platforms
requires_IEEE_754 = unittest.skipUnless( float.getformat("double").startswith("IEEE"), "test requires IEEE 754 doubles") TESTDATADIR = 'decimaltestdata' if name == 'main': file = sys.argv[0] else: file = file testdir = os.path.dirname(file) or os.curdir directory = testdir + os.sep + TESTDATADIR + os.sep skip_expected = not os.path.isdir(directory)
list of individual .decTest test ids that correspond to tests that
we're skipping for one reason or another.
skipped_test_ids = [ 'scbx164', # skipping apparently implementation-specific scaleb 'scbx165', # tests, pending clarification of scaleb rules. ]
XXX: BEGIN CDECIMAL SKIPS
cdecimal_status_diff = {} cdecimal_result_diff = {} cdecimal_skipped_disagreements = [] if HAVE_CDECIMAL: if HAVE_CONFIG_64: # 64 bit version: With a reduced working precision in mpd_qpow() # the status matches. cdecimal_status_diff = { "pwsx803": ([Inexact, Rounded, Subnormal, Underflow], [Inexact, Rounded]), "pwsx805": ([Inexact, Rounded, Subnormal, Underflow], [Inexact, Rounded]) } # These are skipped for decNumber, too. cdecimal_result_diff = { "powx4302": ("1.000000", "1.000001"), "powx4303": ("1.000000", "1.000001"), "powx4342": ("1.000000", "0.9999999"), "powx4343": ("1.000000", "0.9999999") } # Disagreements: # http://bugs.python.org/issue7049[](#l162) cdecimal_skipped_disagreements = ['pwmx325', 'pwmx326']
END CDECIMAL SKIPS
Make sure it actually raises errors when not expected and caught in flags
Slower, since it runs some things several times.
EXTENDEDERRORTEST = False #Map the test cases' error names to the actual errors ErrorNames = {'clamped' : Clamped, 'conversion_syntax' : InvalidOperation, 'division_by_zero' : DivisionByZero, 'division_impossible' : InvalidOperation, 'division_undefined' : InvalidOperation, 'inexact' : Inexact, 'invalid_context' : InvalidOperation, 'invalid_operation' : InvalidOperation, 'overflow' : Overflow, 'rounded' : Rounded, 'subnormal' : Subnormal, 'underflow' : Underflow} def Nonfunction(*args): """Doesn't do anything.""" return None RoundingDict = {'ceiling' : ROUND_CEILING, #Maps test-case names to roundings. 'down' : ROUND_DOWN, 'floor' : ROUND_FLOOR, 'half_down' : ROUND_HALF_DOWN, 'half_even' : ROUND_HALF_EVEN, 'half_up' : ROUND_HALF_UP, 'up' : ROUND_UP, '05up' : ROUND_05UP}
Name adapter to be able to change the Decimal and Context
interface without changing the test files from Cowlishaw
nameAdapter = {'and':'logical_and', 'apply':'_apply', 'class':'number_class', 'comparesig':'compare_signal', 'comparetotal':'compare_total', 'comparetotmag':'compare_total_mag', 'copy':'copy_decimal', 'copyabs':'copy_abs', 'copynegate':'copy_negate', 'copysign':'copy_sign', 'divideint':'divide_int', 'invert':'logical_invert', 'iscanonical':'is_canonical', 'isfinite':'is_finite', 'isinfinite':'is_infinite', 'isnan':'is_nan', 'isnormal':'is_normal', 'isqnan':'is_qnan', 'issigned':'is_signed', 'issnan':'is_snan', 'issubnormal':'is_subnormal', 'iszero':'is_zero', 'maxmag':'max_mag', 'minmag':'min_mag', 'nextminus':'next_minus', 'nextplus':'next_plus', 'nexttoward':'next_toward', 'or':'logical_or', 'reduce':'normalize', 'remaindernear':'remainder_near', 'samequantum':'same_quantum', 'squareroot':'sqrt', 'toeng':'to_eng_string', 'tointegral':'to_integral_value', 'tointegralx':'to_integral_exact', 'tosci':'to_sci_string', 'xor':'logical_xor', }
The following functions return True/False rather than a Decimal instance
LOGICAL_FUNCTIONS = ( 'is_canonical', 'is_finite', 'is_infinite', 'is_nan', 'is_normal', 'is_qnan', 'is_signed', 'is_snan', 'is_subnormal', 'is_zero', 'same_quantum', )
For some operations (currently exp, ln, log10, power), the decNumber
reference implementation imposes additional restrictions on the
context and operands. These restrictions are not part of the
specification; however, the effect of these restrictions does show
up in some of the testcases. We skip testcases that violate these
restrictions, since Decimal behaves differently from decNumber for
these testcases so these testcases would otherwise fail.
decNumberRestricted = ('power', 'ln', 'log10', 'exp') DEC_MAX_MATH = 999999 def outside_decNumber_bounds(v, context): if (context.prec > DEC_MAX_MATH or context.Emax > DEC_MAX_MATH or -context.Emin > DEC_MAX_MATH): return True if not is_special(v) and v and ( v.adjusted() > DEC_MAX_MATH or v.adjusted() < 1-2*DEC_MAX_MATH): return True return False class DecimalTest(unittest.TestCase): """Class which tests the Decimal class against the test cases. Changed for unittest. """ def setUp(self): self.context = Context() self.ignore_list = ['#'] # Basically, a # means return NaN InvalidOperation. # Different from a sNaN in trim self.ChangeDict = {'precision' : self.change_precision, 'rounding' : self.change_rounding_method, 'maxexponent' : self.change_max_exponent, 'minexponent' : self.change_min_exponent, 'clamp' : self.change_clamp} def eval_file(self, file): global skip_expected if skip_expected: raise unittest.SkipTest return with open(file) as f: for line in f: line = line.replace('\r\n', '').replace('\n', '') #print line try: t = self.eval_line(line) except DecimalException as exception: #Exception raised where there shoudn't have been one. self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line) return def eval_line(self, s): if s.find(' -> ') >= 0 and s[:2] != '--' and not s.startswith(' --'): s = (s.split('->')[0] + '->' + s.split('->')[1].split('--')[0]).strip() else: s = s.split('--')[0].strip() for ignore in self.ignore_list: if s.find(ignore) >= 0: #print s.split()[0], 'NotImplemented--', ignore return if not s: return elif ':' in s: return self.eval_directive(s) else: return self.eval_equation(s) def eval_directive(self, s): funct, value = (x.strip().lower() for x in s.split(':')) if funct == 'rounding': value = RoundingDict[value] else: try: value = int(value) except ValueError: pass funct = self.ChangeDict.get(funct, Nonfunction) funct(value) def eval_equation(self, s): #global DEFAULT_PRECISION #print DEFAULT_PRECISION if not TEST_ALL and random.random() < 0.90: return try: Sides = s.split('->') L = Sides[0].strip().split() id = L[0] if DEBUG: print("Test ", id, end=" ") funct = L[1].lower() valstemp = L[2:] L = Sides[1].strip().split() ans = L[0] exceptions = L[1:] except (TypeError, AttributeError, IndexError): raise InvalidOperation def FixQuotes(val): val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote') val = val.replace("'", '').replace('"', '') val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"') return val if id in skipped_test_ids or id in cdecimal_skipped_disagreements: # XXX return fname = nameAdapter.get(funct, funct) if fname == 'rescale': return funct = getattr(self.context, fname) vals = [] conglomerate = '' quote = 0 theirexceptions = [ErrorNames[x.lower()] for x in exceptions] if id in cdecimal_status_diff: # XXX t = cdecimal_status_diff[id] theirexceptions = t[0] for exception in Signals: self.context.traps[exception] = 1 #Catch these bugs... for exception in theirexceptions: self.context.traps[exception] = 0 for i, val in enumerate(valstemp): if val.count("'") % 2 == 1: quote = 1 - quote if quote: conglomerate = conglomerate + ' ' + val continue else: val = conglomerate + val conglomerate = '' v = FixQuotes(val) if fname in ('to_sci_string', 'to_eng_string'): if EXTENDEDERRORTEST: for error in theirexceptions: self.context.traps[error] = 1 try: funct(self.context.create_decimal(v)) except error: pass except Signals as e: self.fail("Raised %s in %s when %s disabled" % [](#l405) (e, s, error)) else: self.fail("Did not raise %s in %s" % (error, s)) self.context.traps[error] = 0 v = self.context.create_decimal(v) else: v = read_unlimited(v, self.context) # XXX vals.append(v) ans = FixQuotes(ans) # XXX: three argument power/powmod if HAVE_CDECIMAL: if fname == 'power' and len(vals) == 3: # name is different fname = 'powmod' funct = getattr(self.context, fname) # skip tests that are related to bounds imposed in the decNumber # reference implementation if fname in decNumberRestricted: if fname == 'power': if not (is_integer(vals[1]) and -1999999997 <= vals[1] <= 999999999): if outside_decNumber_bounds(vals[0], self.context) or [](#l430) outside_decNumber_bounds(vals[1], self.context): #print "Skipping test %s" % s return else: if outside_decNumber_bounds(vals[0], self.context): #print "Skipping test %s" % s return if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'): for error in theirexceptions: self.context.traps[error] = 1 try: funct(*vals) except error: pass except Signals as e: self.fail("Raised %s in %s when %s disabled" % [](#l448) (e, s, error)) else: self.fail("Did not raise %s in %s" % (error, s)) self.context.traps[error] = 0 # as above, but add traps cumulatively, to check precedence ordered_errors = [e for e in OrderedSignals if e in theirexceptions] for error in ordered_errors: self.context.traps[error] = 1 try: funct(*vals) except error: pass except Signals as e: self.fail("Raised %s in %s; expected %s" % (type(e), s, error)) else: self.fail("Did not raise %s in %s" % (error, s)) # reset traps for error in ordered_errors: self.context.traps[error] = 0 if DEBUG: print("--", self.context) try: result = str(funct(*vals)) if fname in LOGICAL_FUNCTIONS: result = str(int(eval(result))) # 'True', 'False' -> '1', '0' except Signals as error: self.fail("Raised %s in %s" % (error, s)) except: #Catch any error long enough to state the test case. print("ERROR:", s) raise myexceptions = self.getexceptions() self.context.clear_flags() myexceptions.sort(key=repr) theirexceptions.sort(key=repr) if id in cdecimal_result_diff: # XXX t = cdecimal_result_diff[id] self.assertTrue(result == t[0] and ans == t[1]) else: self.assertEqual(result, ans, 'Incorrect answer for ' + s + ' -- got ' + result) self.assertEqual(myexceptions, theirexceptions, 'Incorrect flags set in ' + s + ' -- got ' + str(myexceptions)) return def getexceptions(self): return [e for e in Signals if self.context.flags[e]] def change_precision(self, prec): if (not HAVE_CDECIMAL) or HAVE_CONFIG_64: self.context.prec = prec else: # XXX self.context.unsafe_setprec(prec) def change_rounding_method(self, rounding): self.context.rounding = rounding def change_min_exponent(self, exp): if (not HAVE_CDECIMAL) or HAVE_CONFIG_64: self.context.Emin = exp else: # XXX self.context.unsafe_setemin(exp) def change_max_exponent(self, exp): if (not HAVE_CDECIMAL) or HAVE_CONFIG_64: self.context.Emax = exp else: # XXX self.context.unsafe_setemax(exp) def change_clamp(self, clamp): self.context._clamp = clamp
The following classes test the behaviour of Decimal according to PEP 327
class DecimalExplicitConstructionTest(unittest.TestCase):
'''Unit tests for Explicit Construction cases of Decimal.'''
def test_explicit_empty(self):
self.assertEqual(Decimal(), Decimal("0"))
def test_explicit_from_None(self):
self.assertRaises(TypeError, Decimal, None)
def test_explicit_from_int(self):
#positive
d = Decimal(45)
self.assertEqual(str(d), '45')
#very large positive
d = Decimal(500000123)
self.assertEqual(str(d), '500000123')
#negative
d = Decimal(-45)
self.assertEqual(str(d), '-45')
#zero
d = Decimal(0)
self.assertEqual(str(d), '0')
def test_explicit_from_string(self):
#empty
self.assertEqual(str(Decimal('')), 'NaN')
#int
self.assertEqual(str(Decimal('45')), '45')
#float
self.assertEqual(str(Decimal('45.34')), '45.34')
#engineer notation
self.assertEqual(str(Decimal('45e2')), '4.5E+3')
#just not a number
self.assertEqual(str(Decimal('ugly')), 'NaN')
#leading and trailing whitespace permitted
self.assertEqual(str(Decimal('1.3E4 \n')), '1.3E+4')
self.assertEqual(str(Decimal(' -7.89')), '-7.89')
self.assertEqual(str(Decimal(" 3.45679 ")), '3.45679')
with localcontext() as c:
c.traps[InvalidOperation] = True
# Invalid string
self.assertRaises(InvalidOperation, Decimal, "xyz")
# Two arguments max
self.assertRaises(TypeError, Decimal, "1234", "x", "y")
if HAVE_CDECIMAL:
# Too large for cdecimal to be converted exactly
self.assertRaises(InvalidOperation, Decimal,
"1e9999999999999999999")
def test_explicit_from_tuples(self):
#zero
d = Decimal( (0, (0,), 0) )
self.assertEqual(str(d), '0')
#int
d = Decimal( (1, (4, 5), 0) )
self.assertEqual(str(d), '-45')
#float
d = Decimal( (0, (4, 5, 3, 4), -2) )
self.assertEqual(str(d), '45.34')
#weird
d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
self.assertEqual(str(d), '-4.34913534E-17')
#inf
d = Decimal( (0, (), "F") )
self.assertEqual(str(d), 'Infinity')
#wrong number of items
self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) )
#bad sign
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
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
self.assertRaises(ValueError, Decimal, (1, "xyz", 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, 10, 4, 9, 1), 2) )
self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 'a', 1), 2) )
def test_explicit_from_bool(self):
self.assertIs(bool(Decimal(0)), False)
self.assertIs(bool(Decimal(1)), True)
self.assertEqual(Decimal(False), Decimal(0))
self.assertEqual(Decimal(True), Decimal(1))
def test_explicit_from_Decimal(self):
#positive
d = Decimal(45)
e = Decimal(d)
self.assertEqual(str(e), '45')
#very large positive
d = Decimal(500000123)
e = Decimal(d)
self.assertEqual(str(e), '500000123')
#negative
d = Decimal(-45)
e = Decimal(d)
self.assertEqual(str(e), '-45')
#zero
d = Decimal(0)
e = Decimal(d)
self.assertEqual(str(e), '0')
@requires_IEEE_754
def test_explicit_from_float(self):
if HAVE_CDECIMAL:
with localcontext() as c:
c.clear_flags()
self.assertEqual(Decimal(7.5), 7.5)
self.assertTrue(c.flags[FloatOperation])
c.traps[FloatOperation] = True
self.assertRaises(FloatOperation, Decimal, 7.5)
self.assertTrue(c.flags[FloatOperation])
r = Decimal(0.1)
self.assertEqual(type(r), Decimal)
self.assertEqual(str(r),
'0.1000000000000000055511151231257827021181583404541015625')
self.assertTrue(Decimal(float('nan')).is_qnan())
self.assertTrue(Decimal(float('inf')).is_infinite())
self.assertTrue(Decimal(float('-inf')).is_infinite())
self.assertEqual(str(Decimal(float('nan'))),
str(Decimal('NaN')))
self.assertEqual(str(Decimal(float('inf'))),
str(Decimal('Infinity')))
self.assertEqual(str(Decimal(float('-inf'))),
str(Decimal('-Infinity')))
self.assertEqual(str(Decimal(float('-0.0'))),
str(Decimal('-0')))
for i in range(200):
x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0)
self.assertEqual(x, float(Decimal(x))) # roundtrip
def test_explicit_context_create_decimal(self):
nc = copy.copy(getcontext())
nc.prec = 3
# empty
d = Decimal()
self.assertEqual(str(d), '0')
d = nc.create_decimal()
self.assertEqual(str(d), '0')
# from None
self.assertRaises(TypeError, nc.create_decimal, None)
# from int
d = nc.create_decimal(456)
self.assertIsInstance(d, Decimal)
self.assertEqual(nc.create_decimal(45678),
nc.create_decimal('457E+2'))
# from string
d = Decimal('456789')
self.assertEqual(str(d), '456789')
d = nc.create_decimal('456789')
self.assertEqual(str(d), '4.57E+5')
# leading and trailing whitespace should result in a NaN;
# spaces are already checked in Cowlishaw's test-suite, so
# here we just check that a trailing newline results in a NaN
self.assertEqual(str(nc.create_decimal('3.14\n')), 'NaN')
# from tuples
d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
self.assertEqual(str(d), '-4.34913534E-17')
d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
self.assertEqual(str(d), '-4.35E-17')
# from Decimal
prevdec = Decimal(500000123)
d = Decimal(prevdec)
self.assertEqual(str(d), '500000123')
d = nc.create_decimal(prevdec)
self.assertEqual(str(d), '5.00E+8')
# more integers
nc.prec = 28
nc.traps[InvalidOperation] = True
for v in [-263-1, -263, -231-1, -2**31, 0,
231-1, 231, 263-1, 263]:
d = nc.create_decimal(v)
self.assertTrue(isinstance(d, Decimal))
self.assertEqual(int(d), v)
nc.prec = 3
nc.traps[Rounded] = True
self.assertRaises(Rounded, nc.create_decimal, 1234)
# from string
nc.prec = 28
self.assertEqual(str(nc.create_decimal('0E-017')), '0E-17')
self.assertEqual(str(nc.create_decimal('45')), '45')
self.assertEqual(str(nc.create_decimal('-Inf')), '-Infinity')
self.assertEqual(str(nc.create_decimal('NaN123')), 'NaN123')
# invalid arguments
self.assertRaises(InvalidOperation, nc.create_decimal, "xyz")
self.assertRaises(ValueError, nc.create_decimal, (1, "xyz", -25))
if HAVE_CDECIMAL: # decimal.py accepts lists.
self.assertRaises(TypeError, nc.create_decimal, ["%"])
self.assertRaises(TypeError, nc.create_decimal, "1234", "5678")
def test_explicit_context_create_from_float(self):
nc = Context()
if HAVE_CDECIMAL:
nc.clear_flags()
self.assertEqual(nc.create_decimal(7.5), 7.5)
self.assertTrue(nc.flags[FloatOperation])
nc.traps[FloatOperation] = True
self.assertRaises(FloatOperation, nc.create_decimal, 7.5)
self.assertTrue(nc.flags[FloatOperation])
nc.traps[FloatOperation] = False
r = nc.create_decimal(0.1)
self.assertEqual(type(r), Decimal)
self.assertEqual(str(r), '0.1000000000000000055511151231')
self.assertTrue(nc.create_decimal(float('nan')).is_qnan())
self.assertTrue(nc.create_decimal(float('inf')).is_infinite())
self.assertTrue(nc.create_decimal(float('-inf')).is_infinite())
self.assertEqual(str(nc.create_decimal(float('nan'))),
str(nc.create_decimal('NaN')))
self.assertEqual(str(nc.create_decimal(float('inf'))),
str(nc.create_decimal('Infinity')))
self.assertEqual(str(nc.create_decimal(float('-inf'))),
str(nc.create_decimal('-Infinity')))
self.assertEqual(str(nc.create_decimal(float('-0.0'))),
str(nc.create_decimal('-0')))
nc.prec = 100
for i in range(200):
x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0)
self.assertEqual(x, float(nc.create_decimal(x))) # roundtrip
def test_unicode_digits(self):
test_values = {
'\uff11': '1',
'\u0660.\u0660\u0663\u0667\u0662e-\u0663' : '0.0000372',
'-nan\u0c68\u0c6a\u0c66\u0c66' : '-NaN2400',
}
for input, expected in test_values.items():
self.assertEqual(str(Decimal(input)), expected)
class DecimalImplicitConstructionTest(unittest.TestCase):
'''Unit tests for Implicit Construction cases of Decimal.'''
def test_implicit_from_None(self):
self.assertRaises(TypeError, eval, 'Decimal(5) + None', globals())
def test_implicit_from_int(self):
#normal
self.assertEqual(str(Decimal(5) + 45), '50')
#exceeding precision
self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000))
def test_implicit_from_string(self):
self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', globals())
def test_implicit_from_float(self):
self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', globals())
def test_implicit_from_Decimal(self):
self.assertEqual(Decimal(5) + Decimal(45), Decimal(50))
def test_rop(self):
# Allow other classes to be trained to interact with Decimals
class E:
def divmod(self, other):
return 'divmod ' + str(other)
def rdivmod(self, other):
return str(other) + ' rdivmod'
def lt(self, other):
return 'lt ' + str(other)
def gt(self, other):
return 'gt ' + str(other)
def le(self, other):
return 'le ' + str(other)
def ge(self, other):
return 'ge ' + str(other)
def eq(self, other):
return 'eq ' + str(other)
def ne(self, other):
return 'ne ' + str(other)
self.assertEqual(divmod(E(), Decimal(10)), 'divmod 10')
self.assertEqual(divmod(Decimal(10), E()), '10 rdivmod')
self.assertEqual(eval('Decimal(10) < E()'), 'gt 10')
self.assertEqual(eval('Decimal(10) > E()'), 'lt 10')
self.assertEqual(eval('Decimal(10) <= E()'), 'ge 10')
self.assertEqual(eval('Decimal(10) >= E()'), 'le 10')
self.assertEqual(eval('Decimal(10) == E()'), 'eq 10')
self.assertEqual(eval('Decimal(10) != E()'), 'ne 10')
# insert operator methods and then exercise them
oplist = [
('+', 'add', 'radd'),
('-', 'sub', 'rsub'),
('*', 'mul', 'rmul'),
('/', 'truediv', 'rtruediv'),
('%', 'mod', 'rmod'),
('//', 'floordiv', 'rfloordiv'),
('', 'pow', 'rpow')
]
for sym, lop, rop in oplist:
setattr(E, lop, lambda self, other: 'str' + lop + str(other))
setattr(E, rop, lambda self, other: str(other) + rop + 'str')
self.assertEqual(eval('E()' + sym + 'Decimal(10)'),
'str' + lop + '10')
self.assertEqual(eval('Decimal(10)' + sym + 'E()'),
'10' + rop + 'str')
class DecimalFormatTest(unittest.TestCase):
'''Unit tests for the format function.'''
def test_formatting(self):
# triples giving a format, a Decimal, and the expected result
test_values = [
('e', '0E-15', '0e-15'),
('e', '2.3E-15', '2.3e-15'),
('e', '2.30E+2', '2.30e+2'), # preserve significant zeros
('e', '2.30000E-15', '2.30000e-15'),
('e', '1.23456789123456789e40', '1.23456789123456789e+40'),
('e', '1.5', '1.5e+0'),
('e', '0.15', '1.5e-1'),
('e', '0.015', '1.5e-2'),
('e', '0.0000000000015', '1.5e-12'),
('e', '15.0', '1.50e+1'),
('e', '-15', '-1.5e+1'),
('e', '0', '0e+0'),
('e', '0E1', '0e+1'),
('e', '0.0', '0e-1'),
('e', '0.00', '0e-2'),
('.6e', '0E-15', '0.000000e-9'),
('.6e', '0', '0.000000e+6'),
('.6e', '9.999999', '9.999999e+0'),
('.6e', '9.9999999', '1.000000e+1'),
('.6e', '-1.23e5', '-1.230000e+5'),
('.6e', '1.23456789e-3', '1.234568e-3'),
('f', '0', '0'),
('f', '0.0', '0.0'),
('f', '0E-2', '0.00'),
('f', '0.00E-8', '0.0000000000'),
('f', '0E1', '0'), # loses exponent information
('f', '3.2E1', '32'),
('f', '3.2E2', '320'),
('f', '3.20E2', '320'),
('f', '3.200E2', '320.0'),
('f', '3.2E-6', '0.0000032'),
('.6f', '0E-15', '0.000000'), # all zeros treated equally
('.6f', '0E1', '0.000000'),
('.6f', '0', '0.000000'),
('.0f', '0', '0'), # no decimal point
('.0f', '0e-2', '0'),
('.0f', '3.14159265', '3'),
('.1f', '3.14159265', '3.1'),
('.4f', '3.14159265', '3.1416'),
('.6f', '3.14159265', '3.141593'),
('.7f', '3.14159265', '3.1415926'), # round-half-even
('.8f', '3.14159265', '3.14159265'),
('.9f', '3.14159265', '3.141592650'),
('g', '0', '0'),
('g', '0.0', '0.0'),
('g', '0E1', '0e+1'),
('G', '0E1', '0E+1'),
('g', '0E-5', '0.00000'),
('g', '0E-6', '0.000000'),
('g', '0E-7', '0e-7'),
('g', '-0E2', '-0e+2'),
('.0g', '3.14159265', '3'), # 0 sig fig -> 1 sig fig
('.1g', '3.14159265', '3'),
('.2g', '3.14159265', '3.1'),
('.5g', '3.14159265', '3.1416'),
('.7g', '3.14159265', '3.141593'),
('.8g', '3.14159265', '3.1415926'), # round-half-even
('.9g', '3.14159265', '3.14159265'),
('.10g', '3.14159265', '3.14159265'), # don't pad
('%', '0E1', '0%'),
('%', '0E0', '0%'),
('%', '0E-1', '0%'),
('%', '0E-2', '0%'),
('%', '0E-3', '0.0%'),
('%', '0E-4', '0.00%'),
('.3%', '0', '0.000%'), # all zeros treated equally
('.3%', '0E10', '0.000%'),
('.3%', '0E-10', '0.000%'),
('.3%', '2.34', '234.000%'),
('.3%', '1.234567', '123.457%'),
('.0%', '1.23', '123%'),
('e', 'NaN', 'NaN'),
('f', '-NaN123', '-NaN123'),
('+g', 'NaN456', '+NaN456'),
('.3e', 'Inf', 'Infinity'),
('.16f', '-Inf', '-Infinity'),
('.0g', '-sNaN', '-sNaN'),
('', '1.00', '1.00'),
# test alignment and padding
('6', '123', ' 123'),
('<6', '123', '123 '),
('>6', '123', ' 123'),
('^6', '123', ' 123 '),
('=+6', '123', '+ 123'),
('#<10', 'NaN', 'NaN#######'),
('#<10', '-4.3', '-4.3######'),
('#<+10', '0.0130', '+0.0130###'),
('#< 10', '0.0130', ' 0.0130###'),
('@>10', '-Inf', '@-Infinity'),
('#>5', '-Inf', '-Infinity'),
('?^5', '123', '?123?'),
('%^6', '123', '%123%%'),
(' ^6', '-45.6', '-45.6 '),
('/=10', '-45.6', '-/////45.6'),
('/=+10', '45.6', '+/////45.6'),
('/= 10', '45.6', ' /////45.6'),
# thousands separator
(',', '1234567', '1,234,567'),
(',', '123456', '123,456'),
(',', '12345', '12,345'),
(',', '1234', '1,234'),
(',', '123', '123'),
(',', '12', '12'),
(',', '1', '1'),
(',', '0', '0'),
(',', '-1234567', '-1,234,567'),
(',', '-123456', '-123,456'),
('7,', '123456', '123,456'),
('8,', '123456', ' 123,456'),
('08,', '123456', '0,123,456'), # special case: extra 0 needed
('+08,', '123456', '+123,456'), # but not if there's a sign
(' 08,', '123456', ' 123,456'),
('08,', '-123456', '-123,456'),
('+09,', '123456', '+0,123,456'),
# ... with fractional part...
('07,', '1234.56', '1,234.56'),
('08,', '1234.56', '1,234.56'),
('09,', '1234.56', '01,234.56'),
('010,', '1234.56', '001,234.56'),
('011,', '1234.56', '0,001,234.56'),
('012,', '1234.56', '0,001,234.56'),
('08,.1f', '1234.5', '01,234.5'),
# no thousands separators in fraction part
(',', '1.23456789', '1.23456789'),
(',%', '123.456789', '12,345.6789%'),
(',e', '123456', '1.23456e+5'),
(',E', '123456', '1.23456E+5'),
# issue 6850
('a=-7.0', '0.12345', 'aaaa0.1'),
]
for fmt, d, result in test_values:
self.assertEqual(format(Decimal(d), fmt), result)
def test_n_format(self):
try:
from locale import CHAR_MAX
except ImportError:
return
# Set up some localeconv-like dictionaries
en_US = {
'decimal_point' : '.',
'grouping' : make_grouping([3, 3, 0]),
'thousands_sep' : ','
}
fr_FR = {
'decimal_point' : ',',
'grouping' : make_grouping([CHAR_MAX]),
'thousands_sep' : ''
}
ru_RU = {
'decimal_point' : ',',
'grouping': make_grouping([3, 3, 0]),
'thousands_sep' : ' '
}
crazy = {
'decimal_point' : '&',
'grouping': make_grouping([1, 4, 2, CHAR_MAX]),
'thousands_sep' : '-'
}
dotsep_wide = {
'decimal_point' : b'\xc2\xbf'.decode('utf-8'),
'grouping': make_grouping([3, 3, 0]),
'thousands_sep' : b'\xc2\xb4'.decode('utf-8')
}
self.assertEqual(get_fmt(Decimal('12.7'), en_US), '12.7')
self.assertEqual(get_fmt(Decimal('12.7'), fr_FR), '12,7')
self.assertEqual(get_fmt(Decimal('12.7'), ru_RU), '12,7')
self.assertEqual(get_fmt(Decimal('12.7'), crazy), '1-2&7')
self.assertEqual(get_fmt(123456789, en_US), '123,456,789')
self.assertEqual(get_fmt(123456789, fr_FR), '123456789')
self.assertEqual(get_fmt(123456789, ru_RU), '123 456 789')
self.assertEqual(get_fmt(1234567890123, crazy), '123456-78-9012-3')
self.assertEqual(get_fmt(123456789, en_US, '.6n'), '1.23457e+8')
self.assertEqual(get_fmt(123456789, fr_FR, '.6n'), '1,23457e+8')
self.assertEqual(get_fmt(123456789, ru_RU, '.6n'), '1,23457e+8')
self.assertEqual(get_fmt(123456789, crazy, '.6n'), '1&23457e+8')
# zero padding
self.assertEqual(get_fmt(1234, fr_FR, '03n'), '1234')
self.assertEqual(get_fmt(1234, fr_FR, '04n'), '1234')
self.assertEqual(get_fmt(1234, fr_FR, '05n'), '01234')
self.assertEqual(get_fmt(1234, fr_FR, '06n'), '001234')
self.assertEqual(get_fmt(12345, en_US, '05n'), '12,345')
self.assertEqual(get_fmt(12345, en_US, '06n'), '12,345')
self.assertEqual(get_fmt(12345, en_US, '07n'), '012,345')
self.assertEqual(get_fmt(12345, en_US, '08n'), '0,012,345')
self.assertEqual(get_fmt(12345, en_US, '09n'), '0,012,345')
self.assertEqual(get_fmt(12345, en_US, '010n'), '00,012,345')
self.assertEqual(get_fmt(123456, crazy, '06n'), '1-2345-6')
self.assertEqual(get_fmt(123456, crazy, '07n'), '1-2345-6')
self.assertEqual(get_fmt(123456, crazy, '08n'), '1-2345-6')
self.assertEqual(get_fmt(123456, crazy, '09n'), '01-2345-6')
self.assertEqual(get_fmt(123456, crazy, '010n'), '0-01-2345-6')
self.assertEqual(get_fmt(123456, crazy, '011n'), '0-01-2345-6')
self.assertEqual(get_fmt(123456, crazy, '012n'), '00-01-2345-6')
self.assertEqual(get_fmt(123456, crazy, '013n'), '000-01-2345-6')
# wide char separator and decimal point
self.assertEqual(get_fmt(Decimal('-1.5'), dotsep_wide, '020n'),
'-0´000´000´000´001¿5')
class DecimalArithmeticOperatorsTest(unittest.TestCase):
'''Unit tests for all arithmetic operators, binary and unary.'''
def test_addition(self):
d1 = Decimal('-11.1')
d2 = Decimal('22.2')
#two Decimals
self.assertEqual(d1+d2, Decimal('11.1'))
self.assertEqual(d2+d1, Decimal('11.1'))
#with other type, left
c = d1 + 5
self.assertEqual(c, Decimal('-6.1'))
self.assertEqual(type(c), type(d1))
#with other type, right
c = 5 + d1
self.assertEqual(c, Decimal('-6.1'))
self.assertEqual(type(c), type(d1))
#inline with decimal
d1 += d2
self.assertEqual(d1, Decimal('11.1'))
#inline with other type
d1 += 5
self.assertEqual(d1, Decimal('16.1'))
def test_subtraction(self):
d1 = Decimal('-11.1')
d2 = Decimal('22.2')
#two Decimals
self.assertEqual(d1-d2, Decimal('-33.3'))
self.assertEqual(d2-d1, Decimal('33.3'))
#with other type, left
c = d1 - 5
self.assertEqual(c, Decimal('-16.1'))
self.assertEqual(type(c), type(d1))
#with other type, right
c = 5 - d1
self.assertEqual(c, Decimal('16.1'))
self.assertEqual(type(c), type(d1))
#inline with decimal
d1 -= d2
self.assertEqual(d1, Decimal('-33.3'))
#inline with other type
d1 -= 5
self.assertEqual(d1, Decimal('-38.3'))
def test_multiplication(self):
d1 = Decimal('-5')
d2 = Decimal('3')
#two Decimals
self.assertEqual(d1d2, Decimal('-15'))
self.assertEqual(d2d1, Decimal('-15'))
#with other type, left
c = d1 * 5
self.assertEqual(c, Decimal('-25'))
self.assertEqual(type(c), type(d1))
#with other type, right
c = 5 * d1
self.assertEqual(c, Decimal('-25'))
self.assertEqual(type(c), type(d1))
#inline with decimal
d1 *= d2
self.assertEqual(d1, Decimal('-15'))
#inline with other type
d1 *= 5
self.assertEqual(d1, Decimal('-75'))
def test_division(self):
d1 = Decimal('-5')
d2 = Decimal('2')
#two Decimals
self.assertEqual(d1/d2, Decimal('-2.5'))
self.assertEqual(d2/d1, Decimal('-0.4'))
#with other type, left
c = d1 / 4
self.assertEqual(c, Decimal('-1.25'))
self.assertEqual(type(c), type(d1))
#with other type, right
c = 4 / d1
self.assertEqual(c, Decimal('-0.8'))
self.assertEqual(type(c), type(d1))
#inline with decimal
d1 /= d2
self.assertEqual(d1, Decimal('-2.5'))
#inline with other type
d1 /= 4
self.assertEqual(d1, Decimal('-0.625'))
def test_floor_division(self):
d1 = Decimal('5')
d2 = Decimal('2')
#two Decimals
self.assertEqual(d1//d2, Decimal('2'))
self.assertEqual(d2//d1, Decimal('0'))
#with other type, left
c = d1 // 4
self.assertEqual(c, Decimal('1'))
self.assertEqual(type(c), type(d1))
#with other type, right
c = 7 // d1
self.assertEqual(c, Decimal('1'))
self.assertEqual(type(c), type(d1))
#inline with decimal
d1 //= d2
self.assertEqual(d1, Decimal('2'))
#inline with other type
d1 //= 2
self.assertEqual(d1, Decimal('1'))
def test_powering(self):
d1 = Decimal('5')
d2 = Decimal('2')
#two Decimals
self.assertEqual(d1d2, Decimal('25'))
self.assertEqual(d2d1, Decimal('32'))
#with other type, left
c = d1 ** 4
self.assertEqual(c, Decimal('625'))
self.assertEqual(type(c), type(d1))
#with other type, right
c = 7 ** d1
self.assertEqual(c, Decimal('16807'))
self.assertEqual(type(c), type(d1))
#inline with decimal
d1 **= d2
self.assertEqual(d1, Decimal('25'))
#inline with other type
d1 **= 4
self.assertEqual(d1, Decimal('390625'))
def test_module(self):
d1 = Decimal('5')
d2 = Decimal('2')
#two Decimals
self.assertEqual(d1%d2, Decimal('1'))
self.assertEqual(d2%d1, Decimal('2'))
#with other type, left
c = d1 % 4
self.assertEqual(c, Decimal('1'))
self.assertEqual(type(c), type(d1))
#with other type, right
c = 7 % d1
self.assertEqual(c, Decimal('2'))
self.assertEqual(type(c), type(d1))
#inline with decimal
d1 %= d2
self.assertEqual(d1, Decimal('1'))
#inline with other type
d1 %= 4
self.assertEqual(d1, Decimal('1'))
def test_floor_div_module(self):
d1 = Decimal('5')
d2 = Decimal('2')
#two Decimals
(p, q) = divmod(d1, d2)
self.assertEqual(p, Decimal('2'))
self.assertEqual(q, Decimal('1'))
self.assertEqual(type(p), type(d1))
self.assertEqual(type(q), type(d1))
#with other type, left
(p, q) = divmod(d1, 4)
self.assertEqual(p, Decimal('1'))
self.assertEqual(q, Decimal('1'))
self.assertEqual(type(p), type(d1))
self.assertEqual(type(q), type(d1))
#with other type, right
(p, q) = divmod(7, d1)
self.assertEqual(p, Decimal('1'))
self.assertEqual(q, Decimal('2'))
self.assertEqual(type(p), type(d1))
self.assertEqual(type(q), type(d1))
def test_unary_operators(self):
self.assertEqual(+Decimal(45), Decimal(+45)) # +
self.assertEqual(-Decimal(45), Decimal(-45)) # -
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')
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))
self.assertEqual(Decimal(1).copy_sign(-2), d)
self.assertRaises(TypeError, Decimal(1).copy_sign, '-2')
The following are two functions used to test threading in the next class
def thfunc1(cls): d1 = Decimal(1) d3 = Decimal(3) test1 = d1/d3 cls.finish1.set() cls.synchro.wait() test2 = d1/d3 with localcontext() as c2: cls.assertTrue(c2.flags[Inexact]) cls.assertRaises(DivisionByZero, c2.divide, d1, 0) cls.assertTrue(c2.flags[DivisionByZero]) with localcontext() as c3: cls.assertTrue(c3.flags[Inexact]) cls.assertTrue(c3.flags[DivisionByZero]) cls.assertRaises(InvalidOperation, c3.compare, d1, Decimal('sNaN')) cls.assertTrue(c3.flags[InvalidOperation]) del c3 cls.assertFalse(c2.flags[InvalidOperation]) del c2 cls.assertEqual(test1, Decimal('0.333333333333333333333333')) cls.assertEqual(test2, Decimal('0.333333333333333333333333')) c1 = getcontext() cls.assertTrue(c1.flags[Inexact]) for sig in Overflow, Underflow, DivisionByZero, InvalidOperation: cls.assertFalse(c1.flags[sig]) return def thfunc2(cls): d1 = Decimal(1) d3 = Decimal(3) test1 = d1/d3 thiscontext = getcontext() thiscontext.prec = 18 test2 = d1/d3 with localcontext() as c2: cls.assertTrue(c2.flags[Inexact]) cls.assertRaises(Overflow, c2.multiply, Decimal('1e425000000'), 999) cls.assertTrue(c2.flags[Overflow]) with localcontext(thiscontext) as c3: cls.assertTrue(c3.flags[Inexact]) cls.assertFalse(c3.flags[Overflow]) c3.traps[Underflow] = True cls.assertRaises(Underflow, c3.divide, Decimal('1e-425000000'), 999) cls.assertTrue(c3.flags[Underflow]) del c3 cls.assertFalse(c2.flags[Underflow]) cls.assertFalse(c2.traps[Underflow]) del c2 cls.synchro.set() cls.finish2.set() cls.assertEqual(test1, Decimal('0.333333333333333333333333')) cls.assertEqual(test2, Decimal('0.333333333333333333')) cls.assertFalse(thiscontext.traps[Underflow]) cls.assertTrue(thiscontext.flags[Inexact]) for sig in Overflow, Underflow, DivisionByZero, InvalidOperation: cls.assertFalse(thiscontext.flags[sig]) return @unittest.skipUnless(threading, 'threading required') class DecimalUseOfContextTest(unittest.TestCase): '''Unit tests for Use of Context cases in Decimal.''' # Take care executing this test from IDLE, there's an issue in threading # that hangs IDLE and I couldn't find it def test_threading(self): if HAVE_CDECIMAL and not HAVE_THREADS: self.skipTest("compiled without threading") # Test the "threading isolation" of a Context. Also test changing # the DefaultContext, which acts as a template for the thread-local # contexts. save_prec = DefaultContext.prec save_emax = DefaultContext.Emax save_emin = DefaultContext.Emin DefaultContext.prec = 24 DefaultContext.Emax = 425000000 DefaultContext.Emin = -425000000 self.synchro = threading.Event() self.finish1 = threading.Event() self.finish2 = threading.Event() th1 = threading.Thread(target=thfunc1, args=(self,)) th2 = threading.Thread(target=thfunc2, args=(self,)) th1.start() th2.start() self.finish1.wait() self.finish2.wait() for sig in (Inexact, Overflow, Underflow, DivisionByZero, InvalidOperation): self.assertFalse(DefaultContext.flags[sig]) DefaultContext.prec = save_prec DefaultContext.Emax = save_emax DefaultContext.Emin = save_emin return class DecimalUsabilityTest(unittest.TestCase): '''Unit tests for Usability cases of Decimal.''' def test_comparison_operators(self): da = Decimal('23.42') db = Decimal('23.42') dc = Decimal('45') #two Decimals self.assertGreater(dc, da) self.assertGreaterEqual(dc, da) self.assertLess(da, dc) self.assertLessEqual(da, dc) self.assertEqual(da, db) self.assertNotEqual(da, dc) self.assertLessEqual(da, db) self.assertGreaterEqual(da, db) #a Decimal and an int self.assertGreater(dc, 23) self.assertLess(23, dc) self.assertEqual(dc, 45) #a Decimal and uncomparable self.assertNotEqual(da, 'ugly') self.assertNotEqual(da, 32.7) self.assertNotEqual(da, object()) self.assertNotEqual(da, object) # sortable a = list(map(Decimal, range(100))) b = a[:] random.shuffle(a) a.sort() self.assertEqual(a, b) def test_decimal_float_comparison(self): def assert_attr(a, b, attr, context, signal=None): context.clear_flags() f = getattr(a, attr) if HAVE_CDECIMAL: if signal == FloatOperation: self.assertRaises(signal, f, b) else: self.assertIs(f(b), True) self.assertTrue(context.flags[FloatOperation]) else: self.assertIs(f(b), True) small_d = Decimal('0.25') big_d = Decimal('3.0') small_f = 0.25 big_f = 3.0 zero_d = Decimal('0.0') neg_zero_d = Decimal('-0.0') zero_f = 0.0 neg_zero_f = -0.0 inf_d = Decimal('Infinity') neg_inf_d = Decimal('-Infinity') inf_f = float('inf') neg_inf_f = float('-inf') def doit(c, signal=None): # Order for attr in 'lt', 'le': assert_attr(small_d, big_f, attr, c, signal) for attr in 'gt', 'ge': assert_attr(big_d, small_f, attr, c, signal) # Equality assert_attr(small_d, small_f, 'eq', c, None) assert_attr(neg_zero_d, neg_zero_f, 'eq', c, None) assert_attr(neg_zero_d, zero_f, 'eq', c, None) assert_attr(zero_d, neg_zero_f, 'eq', c, None) assert_attr(zero_d, zero_f, 'eq', c, None) assert_attr(neg_inf_d, neg_inf_f, 'eq', c, None) assert_attr(inf_d, inf_f, 'eq', c, None) # Inequality assert_attr(small_d, big_f, 'ne', c, None) assert_attr(Decimal('0.1'), 0.1, 'ne', c, None) assert_attr(neg_inf_d, inf_f, 'ne', c, None) assert_attr(inf_d, neg_inf_f, 'ne', c, None) assert_attr(Decimal('NaN'), float('nan'), 'ne', c, None) def test_containers(c, signal): c.clear_flags() s = set([100.0, Decimal('100.0')]) self.assertEqual(len(s), 1) self.assertTrue(c.flags[FloatOperation]) c.clear_flags() if signal: self.assertRaises(signal, sorted, [1.0, Decimal('10.0')]) else: s = sorted([10.0, Decimal('10.0')]) self.assertTrue(c.flags[FloatOperation]) c.clear_flags() b = 10.0 in [Decimal('10.0'), 1.0] self.assertTrue(c.flags[FloatOperation]) c.clear_flags() b = 10.0 in {Decimal('10.0'):'a', 1.0:'b'} self.assertTrue(c.flags[FloatOperation]) if HAVE_CDECIMAL: nc = Context() with localcontext(nc) as c: sig = None self.assertFalse(c.traps[FloatOperation]) doit(c, signal=sig) test_containers(c, sig) c.traps[FloatOperation] = True doit(c, signal=FloatOperation) test_containers(c, FloatOperation) else: # decimal.py does not have the FloatOperation signal. nc = Context() with localcontext(nc) as c: doit(c, signal=False) def test_copy_and_deepcopy_methods(self): d = Decimal('43.24') c = copy.copy(d) self.assertEqual(id(c), id(d)) dc = copy.deepcopy(d) self.assertEqual(id(dc), id(d)) def test_hash_method(self): def hashit(d): a = hash(d) b = d.hash() self.assertEqual(a, b) return a #just that it's hashable hashit(Decimal(23)) hashit(Decimal('Infinity')) hashit(Decimal('-Infinity')) hashit(Decimal('nan123')) hashit(Decimal('-NaN')) test_values = [Decimal(sign*(2m + n)) for m in [0, 14, 15, 16, 17, 30, 31, 32, 33, 61, 62, 63, 64, 65, 66] for n in range(-10, 10) for sign in [-1, 1]] test_values.extend([ Decimal("-1"), # ==> -2 Decimal("-0"), # zeros Decimal("0.00"), Decimal("-0.000"), Decimal("0E10"), Decimal("-0E12"), Decimal("10.0"), # negative exponent Decimal("-23.00000"), Decimal("1230E100"), # positive exponent Decimal("-4.5678E50"), # a value for which hash(n) != hash(n % (264-1)) # in Python pre-2.6 Decimal(264 + 232 - 1), # selection of values which fail with the old (before # version 2.6) long.hash Decimal("1.634E100"), Decimal("90.697E100"), Decimal("188.83E100"), Decimal("1652.9E100"), Decimal("56531E100"), ]) # check that hash(d) == hash(int(d)) for integral values for value in test_values: self.assertEqual(hashit(value), hashit(int(value))) #the same hash that to an int self.assertEqual(hashit(Decimal(23)), hashit(23)) self.assertRaises(TypeError, hash, Decimal('sNaN')) self.assertTrue(hashit(Decimal('Inf'))) self.assertTrue(hashit(Decimal('-Inf'))) # check that the hashes of a Decimal float match when they # represent exactly the same values test_strings = ['inf', '-Inf', '0.0', '-.0e1', '34.0', '2.5', '112390.625', '-0.515625'] for s in test_strings: f = float(s) d = Decimal(s) self.assertEqual(hashit(f), hashit(d)) # check that the value of the hash doesn't depend on the # current context (issue #1757) c = getcontext() old_precision = c.prec x = Decimal("123456789.1") c.prec = 6 h1 = hashit(x) c.prec = 10 h2 = hashit(x) c.prec = 16 h3 = hashit(x) self.assertEqual(h1, h2) self.assertEqual(h1, h3) c.prec = old_precision def test_min_and_max_methods(self): d1 = Decimal('15.32') d2 = Decimal('28.5') l1 = 15 l2 = 28 #between Decimals self.assertIs(min(d1,d2), d1) self.assertIs(min(d2,d1), d1) self.assertIs(max(d1,d2), d2) self.assertIs(max(d2,d1), d2) #between Decimal and long self.assertIs(min(d1,l2), d1) self.assertIs(min(l2,d1), d1) self.assertIs(max(l1,d2), d2) self.assertIs(max(d2,l1), d2) def test_as_nonzero(self): #as false self.assertFalse(Decimal(0)) #as true self.assertTrue(Decimal('0.372')) def test_tostring_methods(self): #Test str and repr methods. d = Decimal('15.32') self.assertEqual(str(d), '15.32') # str self.assertEqual(repr(d), "Decimal('15.32')") # repr def test_tonum_methods(self): #Test float and int methods. d1 = Decimal('66') d2 = Decimal('15.32') #int self.assertEqual(int(d1), 66) self.assertEqual(int(d2), 15) #float self.assertEqual(float(d1), 66) self.assertEqual(float(d2), 15.32) #floor test_pairs = [ ('123.00', 123), ('3.2', 3), ('3.54', 3), ('3.899', 3), ('-2.3', -3), ('-11.0', -11), ('0.0', 0), ('-0E3', 0), ] for d, i in test_pairs: self.assertEqual(math.floor(Decimal(d)), i) self.assertRaises(ValueError, math.floor, Decimal('-NaN')) self.assertRaises(ValueError, math.floor, Decimal('sNaN')) self.assertRaises(ValueError, math.floor, Decimal('NaN123')) self.assertRaises(OverflowError, math.floor, Decimal('Inf')) self.assertRaises(OverflowError, math.floor, Decimal('-Inf')) #ceiling test_pairs = [ ('123.00', 123), ('3.2', 4), ('3.54', 4), ('3.899', 4), ('-2.3', -2), ('-11.0', -11), ('0.0', 0), ('-0E3', 0), ] for d, i in test_pairs: self.assertEqual(math.ceil(Decimal(d)), i) self.assertRaises(ValueError, math.ceil, Decimal('-NaN')) self.assertRaises(ValueError, math.ceil, Decimal('sNaN')) self.assertRaises(ValueError, math.ceil, Decimal('NaN123')) self.assertRaises(OverflowError, math.ceil, Decimal('Inf')) self.assertRaises(OverflowError, math.ceil, Decimal('-Inf')) #round, single argument test_pairs = [ ('123.00', 123), ('3.2', 3), ('3.54', 4), ('3.899', 4), ('-2.3', -2), ('-11.0', -11), ('0.0', 0), ('-0E3', 0), ('-3.5', -4), ('-2.5', -2), ('-1.5', -2), ('-0.5', 0), ('0.5', 0), ('1.5', 2), ('2.5', 2), ('3.5', 4), ] for d, i in test_pairs: self.assertEqual(round(Decimal(d)), i) self.assertRaises(ValueError, round, Decimal('-NaN')) self.assertRaises(ValueError, round, Decimal('sNaN')) self.assertRaises(ValueError, round, Decimal('NaN123')) self.assertRaises(OverflowError, round, Decimal('Inf')) self.assertRaises(OverflowError, round, Decimal('-Inf')) #round, two arguments; this is essentially equivalent #to quantize, which is already extensively tested test_triples = [ ('123.456', -4, '0E+4'), ('123.456', -3, '0E+3'), ('123.456', -2, '1E+2'), ('123.456', -1, '1.2E+2'), ('123.456', 0, '123'), ('123.456', 1, '123.5'), ('123.456', 2, '123.46'), ('123.456', 3, '123.456'), ('123.456', 4, '123.4560'), ('123.455', 2, '123.46'), ('123.445', 2, '123.44'), ('Inf', 4, 'NaN'), ('-Inf', -23, 'NaN'), ('sNaN314', 3, 'NaN314'), ] for d, n, r in test_triples: self.assertEqual(str(round(Decimal(d), n)), r) def test_eval_round_trip(self): #with zero d = Decimal( (0, (0,), 0) ) self.assertEqual(d, eval(repr(d))) #int d = Decimal( (1, (4, 5), 0) ) self.assertEqual(d, eval(repr(d))) #float d = Decimal( (0, (4, 5, 3, 4), -2) ) self.assertEqual(d, eval(repr(d))) #weird d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) self.assertEqual(d, eval(repr(d))) def test_as_tuple(self): #with zero d = Decimal(0) self.assertEqual(d.as_tuple(), (0, (0,), 0) ) #int d = Decimal(-45) self.assertEqual(d.as_tuple(), (1, (4, 5), 0) ) #complicated string d = Decimal("-4.34913534E-17") self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) # XXX empty tuple, like in the NaN case d = Decimal("Infinity") if HAVE_CDECIMAL: self.assertEqual(d.as_tuple(), (0, (), 'F') ) else: 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') ) # XXX coefficient in infinity should raise an error if not HAVE_CDECIMAL: 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): # Do operations and check that it didn't change change internal objects. # XXX Cdecimal: checked extensively in the library runtest. if HAVE_CDECIMAL: return d1 = Decimal('-25e55') b1 = Decimal('-25e55') d2 = Decimal('33e+33') b2 = Decimal('33e+33') def checkSameDec(operation, useOther=False): if useOther: eval("d1." + operation + "(d2)") self.assertEqual(d1._sign, b1._sign) self.assertEqual(d1._int, b1._int) self.assertEqual(d1._exp, b1._exp) self.assertEqual(d2._sign, b2._sign) self.assertEqual(d2._int, b2._int) self.assertEqual(d2._exp, b2._exp) else: eval("d1." + operation + "()") self.assertEqual(d1._sign, b1._sign) self.assertEqual(d1._int, b1._int) self.assertEqual(d1._exp, b1._exp) return Decimal(d1) self.assertEqual(d1._sign, b1._sign) self.assertEqual(d1._int, b1._int) self.assertEqual(d1._exp, b1._exp) checkSameDec("abs") checkSameDec("add", True) checkSameDec("divmod", True) checkSameDec("eq", True) checkSameDec("ne", True) checkSameDec("le", True) checkSameDec("lt", True) checkSameDec("ge", True) checkSameDec("gt", True) checkSameDec("float") checkSameDec("floordiv", True) checkSameDec("hash") checkSameDec("int") checkSameDec("trunc") checkSameDec("mod", True) checkSameDec("mul", True) checkSameDec("neg") checkSameDec("bool") checkSameDec("pos") checkSameDec("pow", True) checkSameDec("radd", True) checkSameDec("rdivmod", True) checkSameDec("repr") checkSameDec("rfloordiv", True) checkSameDec("rmod", True) checkSameDec("rmul", True) checkSameDec("rpow", True) checkSameDec("rsub", True) checkSameDec("str") checkSameDec("sub", True) checkSameDec("truediv", True) checkSameDec("adjusted") checkSameDec("as_tuple") checkSameDec("compare", True) checkSameDec("max", True) checkSameDec("min", True) checkSameDec("normalize") checkSameDec("quantize", True) checkSameDec("remainder_near", True) checkSameDec("same_quantum", True) checkSameDec("sqrt") checkSameDec("to_eng_string") checkSameDec("to_integral") def test_subclassing(self): # Different behaviours when subclassing Decimal class MyDecimal(Decimal): pass d1 = MyDecimal(1) d2 = MyDecimal(2) d = d1 + d2 self.assertIs(type(d), Decimal) d = d1.max(d2) self.assertIs(type(d), Decimal) def test_implicit_context(self): # Check results when context given implicitly. (Issue 2478) c = getcontext() self.assertEqual(str(Decimal(0).sqrt()), str(c.sqrt(Decimal(0)))) def test_conversions_from_int(self): # Check that methods taking a second Decimal argument will # always accept an integer in place of a Decimal. self.assertEqual(Decimal(4).compare(3), Decimal(4).compare(Decimal(3))) self.assertEqual(Decimal(4).compare_signal(3), Decimal(4).compare_signal(Decimal(3))) self.assertEqual(Decimal(4).compare_total(3), Decimal(4).compare_total(Decimal(3))) self.assertEqual(Decimal(4).compare_total_mag(3), Decimal(4).compare_total_mag(Decimal(3))) self.assertEqual(Decimal(10101).logical_and(1001), Decimal(10101).logical_and(Decimal(1001))) self.assertEqual(Decimal(10101).logical_or(1001), Decimal(10101).logical_or(Decimal(1001))) self.assertEqual(Decimal(10101).logical_xor(1001), Decimal(10101).logical_xor(Decimal(1001))) self.assertEqual(Decimal(567).max(123), Decimal(567).max(Decimal(123))) self.assertEqual(Decimal(567).max_mag(123), Decimal(567).max_mag(Decimal(123))) self.assertEqual(Decimal(567).min(123), Decimal(567).min(Decimal(123))) self.assertEqual(Decimal(567).min_mag(123), Decimal(567).min_mag(Decimal(123))) self.assertEqual(Decimal(567).next_toward(123), Decimal(567).next_toward(Decimal(123))) self.assertEqual(Decimal(1234).quantize(100), Decimal(1234).quantize(Decimal(100))) self.assertEqual(Decimal(768).remainder_near(1234), Decimal(768).remainder_near(Decimal(1234))) self.assertEqual(Decimal(123).rotate(1), Decimal(123).rotate(Decimal(1))) self.assertEqual(Decimal(1234).same_quantum(1000), Decimal(1234).same_quantum(Decimal(1000))) self.assertEqual(Decimal('9.123').scaleb(-100), Decimal('9.123').scaleb(Decimal(-100))) self.assertEqual(Decimal(456).shift(-1), Decimal(456).shift(Decimal(-1))) self.assertEqual(Decimal(-12).fma(Decimal(45), 67), Decimal(-12).fma(Decimal(45), Decimal(67))) self.assertEqual(Decimal(-12).fma(45, 67), Decimal(-12).fma(Decimal(45), Decimal(67))) self.assertEqual(Decimal(-12).fma(45, Decimal(67)), Decimal(-12).fma(Decimal(45), Decimal(67))) class DecimalPythonAPItests(unittest.TestCase): def test_abc(self): self.assertTrue(issubclass(Decimal, numbers.Number)) self.assertTrue(not issubclass(Decimal, numbers.Real)) self.assertIsInstance(Decimal(0), numbers.Number) self.assertNotIsInstance(Decimal(0), numbers.Real) def test_pickle(self): d = Decimal('-3.141590000') p = pickle.dumps(d) e = pickle.loads(p) self.assertEqual(d, e) def test_int(self): for x in range(-250, 250): s = '%0.2f' % (x / 100.0) # should work the same as for floats self.assertEqual(int(Decimal(s)), int(float(s))) # should work the same as to_integral in the ROUND_DOWN mode d = Decimal(s) r = d.to_integral(ROUND_DOWN) self.assertEqual(Decimal(int(d)), r) self.assertRaises(ValueError, int, Decimal('-nan')) self.assertRaises(ValueError, int, Decimal('snan')) self.assertRaises(OverflowError, int, Decimal('inf')) self.assertRaises(OverflowError, int, Decimal('-inf')) def test_trunc(self): for x in range(-250, 250): s = '%0.2f' % (x / 100.0) # should work the same as for floats self.assertEqual(int(Decimal(s)), int(float(s))) # should work the same as to_integral in the ROUND_DOWN mode d = Decimal(s) r = d.to_integral(ROUND_DOWN) self.assertEqual(Decimal(math.trunc(d)), r) def test_from_float(self): class MyDecimal(Decimal): pass r = MyDecimal.from_float(0.1) self.assertEqual(type(r), MyDecimal) self.assertEqual(str(r), '0.1000000000000000055511151231257827021181583404541015625') bigint = 12345678901234567890123456789 self.assertEqual(MyDecimal.from_float(bigint), MyDecimal(bigint)) self.assertTrue(MyDecimal.from_float(float('nan')).is_qnan()) self.assertTrue(MyDecimal.from_float(float('inf')).is_infinite()) self.assertTrue(MyDecimal.from_float(float('-inf')).is_infinite()) self.assertEqual(str(MyDecimal.from_float(float('nan'))), str(Decimal('NaN'))) self.assertEqual(str(MyDecimal.from_float(float('inf'))), str(Decimal('Infinity'))) self.assertEqual(str(MyDecimal.from_float(float('-inf'))), str(Decimal('-Infinity'))) self.assertRaises(TypeError, MyDecimal.from_float, 'abc') for i in range(200): x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0) self.assertEqual(x, float(MyDecimal.from_float(x))) # roundtrip def test_create_decimal_from_float(self): context = Context(prec=5, rounding=ROUND_DOWN) self.assertEqual( context.create_decimal_from_float(math.pi), Decimal('3.1415') ) context = Context(prec=5, rounding=ROUND_UP) self.assertEqual( context.create_decimal_from_float(math.pi), Decimal('3.1416') ) context = Context(prec=5, traps=[Inexact]) self.assertRaises( Inexact, context.create_decimal_from_float, math.pi ) self.assertEqual(repr(context.create_decimal_from_float(-0.0)), "Decimal('-0')") self.assertEqual(repr(context.create_decimal_from_float(1.0)), "Decimal('1')") self.assertEqual(repr(context.create_decimal_from_float(10)), "Decimal('10')") def test_quantize(self): c = Context(Emax=99999, Emin=-99999) self.assertEqual( Decimal('7.335').quantize(Decimal('.01')), Decimal('7.34') ) self.assertEqual( Decimal('7.335').quantize(Decimal('.01'), rounding=ROUND_DOWN), Decimal('7.33') ) self.assertRaises( InvalidOperation, Decimal("10e99999").quantize, Decimal('1e100000'), context=c ) if HAVE_CDECIMAL: self.assertRaises( TypeError, Decimal("1.23456789").quantize, Decimal('1e-100000'), [] ) self.assertRaises( TypeError, Decimal("1.23456789").quantize, Decimal('1e-100000'), c ) self.assertRaises( ValueError, Decimal("1.23456789").quantize, Decimal('1e-100000'), 10 ) self.assertRaises( TypeError, Decimal("1.23456789").quantize, Decimal('1e-100000'), ROUND_UP, 1000 ) class ContextAPItests(unittest.TestCase): def test_pickle(self): c = Context() e = pickle.loads(pickle.dumps(c)) if HAVE_CDECIMAL: # XXX self.assertEqual(c.prec, e.prec) self.assertEqual(c.Emin, e.Emin) self.assertEqual(c.Emax, e.Emax) self.assertEqual(c.rounding, e.rounding) self.assertEqual(c.capitals, e.capitals) self.assertEqual(c.traps, e.traps) self.assertEqual(c.flags, e.flags) self.assertEqual(c._clamp, e._clamp) else: for k in vars(c): v1 = vars(c)[k] v2 = vars(e)[k] self.assertEqual(v1, v2) def test_equality_with_other_types(self): self.assertIn(Decimal(10), ['a', 1.0, Decimal(10), (1,2), {}]) self.assertNotIn(Decimal(10), ['a', 1.0, (1,2), {}]) def test_copy(self): # All copies should be deep c = Context() d = c.copy() self.assertNotEqual(id(c), id(d)) self.assertNotEqual(id(c.flags), id(d.flags)) self.assertNotEqual(id(c.traps), id(d.traps)) self.assertEqual(c.flags, d.flags) def test_abs(self): c = Context() d = c.abs(Decimal(-1)) self.assertEqual(c.abs(-1), d) self.assertRaises(TypeError, c.abs, '-1') def test_add(self): c = Context() d = c.add(Decimal(1), Decimal(1)) self.assertEqual(c.add(1, 1), d) self.assertEqual(c.add(Decimal(1), 1), d) self.assertEqual(c.add(1, Decimal(1)), d) self.assertRaises(TypeError, c.add, '1', 1) self.assertRaises(TypeError, c.add, 1, '1') def test_compare(self): c = Context() d = c.compare(Decimal(1), Decimal(1)) self.assertEqual(c.compare(1, 1), d) self.assertEqual(c.compare(Decimal(1), 1), d) self.assertEqual(c.compare(1, Decimal(1)), d) self.assertRaises(TypeError, c.compare, '1', 1) self.assertRaises(TypeError, c.compare, 1, '1') def test_compare_signal(self): c = Context() d = c.compare_signal(Decimal(1), Decimal(1)) self.assertEqual(c.compare_signal(1, 1), d) self.assertEqual(c.compare_signal(Decimal(1), 1), d) self.assertEqual(c.compare_signal(1, Decimal(1)), d) self.assertRaises(TypeError, c.compare_signal, '1', 1) self.assertRaises(TypeError, c.compare_signal, 1, '1') def test_compare_total(self): c = Context() d = c.compare_total(Decimal(1), Decimal(1)) self.assertEqual(c.compare_total(1, 1), d) self.assertEqual(c.compare_total(Decimal(1), 1), d) self.assertEqual(c.compare_total(1, Decimal(1)), d) self.assertRaises(TypeError, c.compare_total, '1', 1) self.assertRaises(TypeError, c.compare_total, 1, '1') def test_compare_total_mag(self): c = Context() d = c.compare_total_mag(Decimal(1), Decimal(1)) self.assertEqual(c.compare_total_mag(1, 1), d) self.assertEqual(c.compare_total_mag(Decimal(1), 1), d) self.assertEqual(c.compare_total_mag(1, Decimal(1)), d) self.assertRaises(TypeError, c.compare_total_mag, '1', 1) self.assertRaises(TypeError, c.compare_total_mag, 1, '1') def test_copy_abs(self): c = Context() d = c.copy_abs(Decimal(-1)) self.assertEqual(c.copy_abs(-1), d) self.assertRaises(TypeError, c.copy_abs, '-1') def test_copy_decimal(self): c = Context() d = c.copy_decimal(Decimal(-1)) self.assertEqual(c.copy_decimal(-1), d) self.assertRaises(TypeError, c.copy_decimal, '-1') def test_copy_negate(self): c = Context() d = c.copy_negate(Decimal(-1)) self.assertEqual(c.copy_negate(-1), d) self.assertRaises(TypeError, c.copy_negate, '-1') def test_copy_sign(self): c = Context() d = c.copy_sign(Decimal(1), Decimal(-2)) self.assertEqual(c.copy_sign(1, -2), d) self.assertEqual(c.copy_sign(Decimal(1), -2), d) self.assertEqual(c.copy_sign(1, Decimal(-2)), d) self.assertRaises(TypeError, c.copy_sign, '1', -2) self.assertRaises(TypeError, c.copy_sign, 1, '-2') def test_divide(self): c = Context() d = c.divide(Decimal(1), Decimal(2)) self.assertEqual(c.divide(1, 2), d) self.assertEqual(c.divide(Decimal(1), 2), d) self.assertEqual(c.divide(1, Decimal(2)), d) self.assertRaises(TypeError, c.divide, '1', 2) self.assertRaises(TypeError, c.divide, 1, '2') def test_divide_int(self): c = Context() d = c.divide_int(Decimal(1), Decimal(2)) self.assertEqual(c.divide_int(1, 2), d) self.assertEqual(c.divide_int(Decimal(1), 2), d) self.assertEqual(c.divide_int(1, Decimal(2)), d) self.assertRaises(TypeError, c.divide_int, '1', 2) self.assertRaises(TypeError, c.divide_int, 1, '2') def test_divmod(self): c = Context() d = c.divmod(Decimal(1), Decimal(2)) self.assertEqual(c.divmod(1, 2), d) self.assertEqual(c.divmod(Decimal(1), 2), d) self.assertEqual(c.divmod(1, Decimal(2)), d) self.assertRaises(TypeError, c.divmod, '1', 2) self.assertRaises(TypeError, c.divmod, 1, '2') def test_exp(self): c = Context() d = c.exp(Decimal(10)) self.assertEqual(c.exp(10), d) self.assertRaises(TypeError, c.exp, '10') def test_fma(self): c = Context() d = c.fma(Decimal(2), Decimal(3), Decimal(4)) self.assertEqual(c.fma(2, 3, 4), d) self.assertEqual(c.fma(Decimal(2), 3, 4), d) self.assertEqual(c.fma(2, Decimal(3), 4), d) self.assertEqual(c.fma(2, 3, Decimal(4)), d) self.assertEqual(c.fma(Decimal(2), Decimal(3), 4), d) self.assertRaises(TypeError, c.fma, '2', 3, 4) self.assertRaises(TypeError, c.fma, 2, '3', 4) self.assertRaises(TypeError, c.fma, 2, 3, '4') def test_is_finite(self): c = Context() d = c.is_finite(Decimal(10)) self.assertEqual(c.is_finite(10), d) self.assertRaises(TypeError, c.is_finite, '10') def test_is_infinite(self): c = Context() d = c.is_infinite(Decimal(10)) self.assertEqual(c.is_infinite(10), d) self.assertRaises(TypeError, c.is_infinite, '10') def test_is_nan(self): c = Context() d = c.is_nan(Decimal(10)) self.assertEqual(c.is_nan(10), d) self.assertRaises(TypeError, c.is_nan, '10') def test_is_normal(self): c = Context() d = c.is_normal(Decimal(10)) self.assertEqual(c.is_normal(10), d) self.assertRaises(TypeError, c.is_normal, '10') def test_is_qnan(self): c = Context() d = c.is_qnan(Decimal(10)) self.assertEqual(c.is_qnan(10), d) self.assertRaises(TypeError, c.is_qnan, '10') def test_is_signed(self): c = Context() d = c.is_signed(Decimal(10)) self.assertEqual(c.is_signed(10), d) self.assertRaises(TypeError, c.is_signed, '10') def test_is_snan(self): c = Context() d = c.is_snan(Decimal(10)) self.assertEqual(c.is_snan(10), d) self.assertRaises(TypeError, c.is_snan, '10') def test_is_subnormal(self): c = Context() d = c.is_subnormal(Decimal(10)) self.assertEqual(c.is_subnormal(10), d) self.assertRaises(TypeError, c.is_subnormal, '10') def test_is_zero(self): c = Context() d = c.is_zero(Decimal(10)) self.assertEqual(c.is_zero(10), d) self.assertRaises(TypeError, c.is_zero, '10') def test_ln(self): c = Context() d = c.ln(Decimal(10)) self.assertEqual(c.ln(10), d) self.assertRaises(TypeError, c.ln, '10') def test_log10(self): c = Context() d = c.log10(Decimal(10)) self.assertEqual(c.log10(10), d) self.assertRaises(TypeError, c.log10, '10') def test_logb(self): c = Context() d = c.logb(Decimal(10)) self.assertEqual(c.logb(10), d) self.assertRaises(TypeError, c.logb, '10') def test_logical_and(self): c = Context() d = c.logical_and(Decimal(1), Decimal(1)) self.assertEqual(c.logical_and(1, 1), d) self.assertEqual(c.logical_and(Decimal(1), 1), d) self.assertEqual(c.logical_and(1, Decimal(1)), d) self.assertRaises(TypeError, c.logical_and, '1', 1) self.assertRaises(TypeError, c.logical_and, 1, '1') def test_logical_invert(self): c = Context() d = c.logical_invert(Decimal(1000)) self.assertEqual(c.logical_invert(1000), d) self.assertRaises(TypeError, c.logical_invert, '1000') def test_logical_or(self): c = Context() d = c.logical_or(Decimal(1), Decimal(1)) self.assertEqual(c.logical_or(1, 1), d) self.assertEqual(c.logical_or(Decimal(1), 1), d) self.assertEqual(c.logical_or(1, Decimal(1)), d) self.assertRaises(TypeError, c.logical_or, '1', 1) self.assertRaises(TypeError, c.logical_or, 1, '1') def test_logical_xor(self): c = Context() d = c.logical_xor(Decimal(1), Decimal(1)) self.assertEqual(c.logical_xor(1, 1), d) self.assertEqual(c.logical_xor(Decimal(1), 1), d) self.assertEqual(c.logical_xor(1, Decimal(1)), d) self.assertRaises(TypeError, c.logical_xor, '1', 1) self.assertRaises(TypeError, c.logical_xor, 1, '1') def test_max(self): c = Context() d = c.max(Decimal(1), Decimal(2)) self.assertEqual(c.max(1, 2), d) self.assertEqual(c.max(Decimal(1), 2), d) self.assertEqual(c.max(1, Decimal(2)), d) self.assertRaises(TypeError, c.max, '1', 2) self.assertRaises(TypeError, c.max, 1, '2') def test_max_mag(self): c = Context() d = c.max_mag(Decimal(1), Decimal(2)) self.assertEqual(c.max_mag(1, 2), d) self.assertEqual(c.max_mag(Decimal(1), 2), d) self.assertEqual(c.max_mag(1, Decimal(2)), d) self.assertRaises(TypeError, c.max_mag, '1', 2) self.assertRaises(TypeError, c.max_mag, 1, '2') def test_min(self): c = Context() d = c.min(Decimal(1), Decimal(2)) self.assertEqual(c.min(1, 2), d) self.assertEqual(c.min(Decimal(1), 2), d) self.assertEqual(c.min(1, Decimal(2)), d) self.assertRaises(TypeError, c.min, '1', 2) self.assertRaises(TypeError, c.min, 1, '2') def test_min_mag(self): c = Context() d = c.min_mag(Decimal(1), Decimal(2)) self.assertEqual(c.min_mag(1, 2), d) self.assertEqual(c.min_mag(Decimal(1), 2), d) self.assertEqual(c.min_mag(1, Decimal(2)), d) self.assertRaises(TypeError, c.min_mag, '1', 2) self.assertRaises(TypeError, c.min_mag, 1, '2') def test_minus(self): c = Context() d = c.minus(Decimal(10)) self.assertEqual(c.minus(10), d) self.assertRaises(TypeError, c.minus, '10') def test_multiply(self): c = Context() d = c.multiply(Decimal(1), Decimal(2)) self.assertEqual(c.multiply(1, 2), d) self.assertEqual(c.multiply(Decimal(1), 2), d) self.assertEqual(c.multiply(1, Decimal(2)), d) self.assertRaises(TypeError, c.multiply, '1', 2) self.assertRaises(TypeError, c.multiply, 1, '2') def test_next_minus(self): c = Context() d = c.next_minus(Decimal(10)) self.assertEqual(c.next_minus(10), d) self.assertRaises(TypeError, c.next_minus, '10') def test_next_plus(self): c = Context() d = c.next_plus(Decimal(10)) self.assertEqual(c.next_plus(10), d) self.assertRaises(TypeError, c.next_plus, '10') def test_next_toward(self): c = Context() d = c.next_toward(Decimal(1), Decimal(2)) self.assertEqual(c.next_toward(1, 2), d) self.assertEqual(c.next_toward(Decimal(1), 2), d) self.assertEqual(c.next_toward(1, Decimal(2)), d) self.assertRaises(TypeError, c.next_toward, '1', 2) self.assertRaises(TypeError, c.next_toward, 1, '2') def test_normalize(self): c = Context() d = c.normalize(Decimal(10)) self.assertEqual(c.normalize(10), d) self.assertRaises(TypeError, c.normalize, '10') def test_number_class(self): c = Context() self.assertEqual(c.number_class(123), c.number_class(Decimal(123))) self.assertEqual(c.number_class(0), c.number_class(Decimal(0))) self.assertEqual(c.number_class(-45), c.number_class(Decimal(-45))) def test_powmod(self): if not HAVE_CDECIMAL: return c = Context() d = c.powmod(Decimal(1), Decimal(4), Decimal(2)) self.assertEqual(c.powmod(1, 4, 2), d) self.assertEqual(c.powmod(Decimal(1), 4, 2), d) self.assertEqual(c.powmod(1, Decimal(4), 2), d) self.assertEqual(c.powmod(1, 4, Decimal(2)), d) self.assertEqual(c.powmod(Decimal(1), Decimal(4), 2), d) self.assertRaises(TypeError, c.powmod, '1', 4, 2) self.assertRaises(TypeError, c.powmod, 1, '4', 2) self.assertRaises(TypeError, c.powmod, 1, 4, '2') def test_plus(self): c = Context() d = c.plus(Decimal(10)) self.assertEqual(c.plus(10), d) self.assertRaises(TypeError, c.plus, '10') def test_quantize(self): c = Context() d = c.quantize(Decimal(1), Decimal(2)) self.assertEqual(c.quantize(1, 2), d) self.assertEqual(c.quantize(Decimal(1), 2), d) self.assertEqual(c.quantize(1, Decimal(2)), d) self.assertRaises(TypeError, c.quantize, '1', 2) self.assertRaises(TypeError, c.quantize, 1, '2') def test_remainder(self): c = Context() d = c.remainder(Decimal(1), Decimal(2)) self.assertEqual(c.remainder(1, 2), d) self.assertEqual(c.remainder(Decimal(1), 2), d) self.assertEqual(c.remainder(1, Decimal(2)), d) self.assertRaises(TypeError, c.remainder, '1', 2) self.assertRaises(TypeError, c.remainder, 1, '2') def test_remainder_near(self): c = Context() d = c.remainder_near(Decimal(1), Decimal(2)) self.assertEqual(c.remainder_near(1, 2), d) self.assertEqual(c.remainder_near(Decimal(1), 2), d) self.assertEqual(c.remainder_near(1, Decimal(2)), d) self.assertRaises(TypeError, c.remainder_near, '1', 2) self.assertRaises(TypeError, c.remainder_near, 1, '2') def test_rotate(self): c = Context() d = c.rotate(Decimal(1), Decimal(2)) self.assertEqual(c.rotate(1, 2), d) self.assertEqual(c.rotate(Decimal(1), 2), d) self.assertEqual(c.rotate(1, Decimal(2)), d) self.assertRaises(TypeError, c.rotate, '1', 2) self.assertRaises(TypeError, c.rotate, 1, '2') def test_sqrt(self): c = Context() d = c.sqrt(Decimal(10)) self.assertEqual(c.sqrt(10), d) self.assertRaises(TypeError, c.sqrt, '10') def test_same_quantum(self): c = Context() d = c.same_quantum(Decimal(1), Decimal(2)) self.assertEqual(c.same_quantum(1, 2), d) self.assertEqual(c.same_quantum(Decimal(1), 2), d) self.assertEqual(c.same_quantum(1, Decimal(2)), d) self.assertRaises(TypeError, c.same_quantum, '1', 2) self.assertRaises(TypeError, c.same_quantum, 1, '2') def test_scaleb(self): c = Context() d = c.scaleb(Decimal(1), Decimal(2)) self.assertEqual(c.scaleb(1, 2), d) self.assertEqual(c.scaleb(Decimal(1), 2), d) self.assertEqual(c.scaleb(1, Decimal(2)), d) self.assertRaises(TypeError, c.scaleb, '1', 2) self.assertRaises(TypeError, c.scaleb, 1, '2') def test_shift(self): c = Context() d = c.shift(Decimal(1), Decimal(2)) self.assertEqual(c.shift(1, 2), d) self.assertEqual(c.shift(Decimal(1), 2), d) self.assertEqual(c.shift(1, Decimal(2)), d) self.assertRaises(TypeError, c.shift, '1', 2) self.assertRaises(TypeError, c.shift, 1, '2') def test_subtract(self): c = Context() d = c.subtract(Decimal(1), Decimal(2)) self.assertEqual(c.subtract(1, 2), d) self.assertEqual(c.subtract(Decimal(1), 2), d) self.assertEqual(c.subtract(1, Decimal(2)), d) self.assertRaises(TypeError, c.subtract, '1', 2) self.assertRaises(TypeError, c.subtract, 1, '2') def test_to_eng_string(self): c = Context() d = c.to_eng_string(Decimal(10)) self.assertEqual(c.to_eng_string(10), d) self.assertRaises(TypeError, c.to_eng_string, '10') def test_to_sci_string(self): c = Context() d = c.to_sci_string(Decimal(10)) self.assertEqual(c.to_sci_string(10), d) self.assertRaises(TypeError, c.to_sci_string, '10') def test_to_integral_exact(self): c = Context() d = c.to_integral_exact(Decimal(10)) self.assertEqual(c.to_integral_exact(10), d) self.assertRaises(TypeError, c.to_integral_exact, '10') def test_to_integral_value(self): c = Context() d = c.to_integral_value(Decimal(10)) self.assertEqual(c.to_integral_value(10), d) self.assertRaises(TypeError, c.to_integral_value, '10') self.assertRaises(TypeError, c.to_integral_value, 10, 'x') class WithStatementTest(unittest.TestCase): # Can't do these as docstrings until Python 2.6 # as doctest can't handle future statements def test_localcontext(self): # Use a copy of the current context in the block orig_ctx = getcontext() with localcontext() as enter_ctx: set_ctx = getcontext() final_ctx = getcontext() self.assertIs(orig_ctx, final_ctx, 'did not restore context correctly') self.assertIsNot(orig_ctx, set_ctx, 'did not copy the context') self.assertIs(set_ctx, enter_ctx, 'enter returned wrong context') def test_localcontextarg(self): # Use a copy of the supplied context in the block orig_ctx = getcontext() new_ctx = Context(prec=42) with localcontext(new_ctx) as enter_ctx: set_ctx = getcontext() final_ctx = getcontext() self.assertIs(orig_ctx, final_ctx, 'did not restore context correctly') self.assertEqual(set_ctx.prec, new_ctx.prec, 'did not set correct context') self.assertIsNot(new_ctx, set_ctx, 'did not copy the context') self.assertIs(set_ctx, enter_ctx, 'enter returned wrong context') def test_nested_with_statements(self): # Use a copy of the supplied context in the block orig_ctx = getcontext() orig_ctx.clear_flags() new_ctx = Context(Emax=384) with localcontext() as c1: self.assertEqual(c1.flags, orig_ctx.flags) self.assertEqual(c1.traps, orig_ctx.traps) c1.traps[Clamped] = True c1.Emin = -383 self.assertRaises(Clamped, c1.create_decimal, '0e-999') self.assertTrue(c1.flags[Clamped]) with localcontext(new_ctx) as c2: self.assertEqual(c2.flags, new_ctx.flags) self.assertEqual(c2.traps, new_ctx.traps) self.assertRaises(Overflow, c2.power, Decimal('3.4e200'), 2) self.assertFalse(c2.flags[Clamped]) self.assertTrue(c2.flags[Overflow]) del c2 self.assertFalse(c1.flags[Overflow]) del c1 self.assertNotEqual(orig_ctx.Emin, -383) self.assertFalse(orig_ctx.flags[Clamped]) self.assertFalse(orig_ctx.flags[Overflow]) self.assertFalse(new_ctx.flags[Clamped]) self.assertFalse(new_ctx.flags[Overflow]) def test_with_statements_gc1(self): with localcontext() as c1: del c1 with localcontext() as c2: del c2 with localcontext() as c3: del c3 with localcontext() as c4: del c4 def test_with_statements_gc2(self): with localcontext() as c1: with localcontext(c1) as c2: del c1 with localcontext(c2) as c3: del c2 with localcontext(c3) as c4: del c3 del c4 def test_with_statements_gc3(self): with localcontext() as c1: del c1 n1 = Context(prec=1) setcontext(n1) with localcontext(n1) as c2: del n1 self.assertEqual(c2.prec, 1) del c2 n2 = Context(prec=2) setcontext(n2) del n2 self.assertEqual(getcontext().prec, 2) n3 = Context(prec=3) setcontext(n3) self.assertEqual(getcontext().prec, 3) with localcontext(n3) as c3: del n3 self.assertEqual(c3.prec, 3) del c3 n4 = Context(prec=4) setcontext(n4) del n4 self.assertEqual(getcontext().prec, 4) with localcontext() as c4: self.assertEqual(c4.prec, 4) del c4 class ContextFlags(unittest.TestCase): def test_flags_irrelevant(self): # check that the result (numeric result + flags raised) of an # arithmetic operation doesn't depend on the current flags context = Context(prec=9, Emin = -425000000, Emax = 425000000, rounding=ROUND_HALF_EVEN, traps=[], flags=[]) # operations that raise various flags, in the form (function, arglist) operations = [ (context._apply, [Decimal("100E-425000010")]), (context.sqrt, [Decimal(2)]), (context.add, [Decimal("1.23456789"), Decimal("9.87654321")]), (context.multiply, [Decimal("1.23456789"), Decimal("9.87654321")]), (context.subtract, [Decimal("1.23456789"), Decimal("9.87654321")]), ] # try various flags individually, then a whole lot at once flagsets = [[Inexact], [Rounded], [Underflow], [Clamped], [Subnormal], [Inexact, Rounded, Underflow, Clamped, Subnormal]] for fn, args in operations: # find answer and flags raised using a clean context context.clear_flags() ans = fn(*args) flags = [k for k, v in context.flags.items() if v] for extra_flags in flagsets: # set flags, before calling operation context.clear_flags() for flag in extra_flags: raise_error(context, flag) # XXX new_ans = fn(*args) # flags that we expect to be set after the operation expected_flags = list(flags) for flag in extra_flags: if flag not in expected_flags: expected_flags.append(flag) expected_flags.sort(key=id) # flags we actually got new_flags = [k for k,v in context.flags.items() if v] new_flags.sort(key=id) self.assertEqual(ans, new_ans, "operation produces different answers depending on flags set: " + "expected %s, got %s." % (ans, new_ans)) self.assertEqual(new_flags, expected_flags, "operation raises different flags depending on flags set: " + "expected %s, got %s" % (expected_flags, new_flags)) def test_float_operation(self): if not HAVE_CDECIMAL: return context = Context() self.assertFalse(context.flags[FloatOperation]) self.assertFalse(context.traps[FloatOperation]) self.assertFalse(context._flags&DecFloatOperation) self.assertFalse(context._traps&DecFloatOperation) context.settraps([Inexact, FloatOperation]) self.assertEqual(context._traps, DecInexact|DecFloatOperation) self.assertTrue(context.traps[FloatOperation]) self.assertTrue(context.traps[Inexact]) class SpecialContexts(unittest.TestCase): def test_context_templates(self): if HAVE_CDECIMAL: self.assertEqual( BasicContext._traps, DecIEEEInvalidOperation|DecDivisionByZero|DecOverflow| DecUnderflow|DecClamped ) basic_context_prec = BasicContext.prec extended_context_prec = ExtendedContext.prec BasicContext.prec = ExtendedContext.prec = 441 for template in BasicContext, ExtendedContext: setcontext(template) c = getcontext() self.assertIsNot(c, template) self.assertEqual(c.prec, 441) BasicContext.prec = basic_context_prec ExtendedContext.prec = extended_context_prec def test_default_context(self): if HAVE_CDECIMAL: self.assertEqual( DefaultContext._traps, DecIEEEInvalidOperation|DecDivisionByZero|DecOverflow ) default_context_prec = DefaultContext.prec c = getcontext() saveprec = c.prec DefaultContext.prec = 961 c = getcontext() self.assertEqual(c.prec, saveprec) setcontext(DefaultContext) c = getcontext() self.assertIsNot(c, DefaultContext) self.assertEqual(c.prec, 961) DefaultContext.prec = default_context_prec def test_ieee_context(self): if not HAVE_CDECIMAL: return def assert_rest(self, context): self.assertEqual(context.clamp, 1) for v in context.traps.values(): self.assertFalse(v) for v in context.flags.values(): self.assertFalse(v) c = IEEEContext(DECIMAL32) self.assertEqual(c.prec, 7) self.assertEqual(c.Emax, 96) self.assertEqual(c.Emin, -95) assert_rest(self, c) c = IEEEContext(DECIMAL64) self.assertEqual(c.prec, 16) self.assertEqual(c.Emax, 384) self.assertEqual(c.Emin, -383) assert_rest(self, c) c = IEEEContext(DECIMAL128) self.assertEqual(c.prec, 34) self.assertEqual(c.Emax, 6144) self.assertEqual(c.Emin, -6143) assert_rest(self, c) # Invalid values self.assertRaises(OverflowError, IEEEContext, 263) self.assertRaises(ValueError, IEEEContext, -1) self.assertRaises(ValueError, IEEEContext, 1024) class Coverage(unittest.TestCase): def test_invalid_context(self): if not HAVE_CDECIMAL: return c = DefaultContext.copy() int_max = 263-1 if HAVE_CONFIG_64 else 231-1 gt_max_emax = 1018 if HAVE_CONFIG_64 else 109 # OverflowError, general ValueError for attr in ('prec', 'rounding', 'Emin', 'Emax', 'capitals', 'clamp', '_flags', '_traps', '_allcr'): self.assertRaises(OverflowError, setattr, c, attr, int_max+1) self.assertRaises(OverflowError, setattr, c, attr, -int_max-2) if sys.platform != 'win32': self.assertRaises(ValueError, setattr, c, attr, int_max) self.assertRaises(ValueError, setattr, c, attr, -int_max-1) # OverflowError: unsafe_prec, unsafe_emin, unsafe_emax self.assertRaises(OverflowError, getattr(c, 'unsafe_setprec'), int_max+1) self.assertRaises(OverflowError, getattr(c, 'unsafe_setemax'), int_max+1) self.assertRaises(OverflowError, getattr(c, 'unsafe_setemin'), -int_max-2) # Specific: prec, Emax for attr in ['prec', 'Emax']: setattr(c, attr, 999999) self.assertEqual(getattr(c, attr), 999999) self.assertRaises(ValueError, setattr, c, attr, -1) self.assertRaises(ValueError, setattr, c, attr, gt_max_emax) self.assertRaises(TypeError, setattr, c, attr, 'xyz') # Specific: Emin setattr(c, 'Emin', -999999) self.assertEqual(getattr(c, 'Emin'), -999999) self.assertRaises(ValueError, setattr, c, 'Emin', 1) self.assertRaises(ValueError, setattr, c, 'Emin', -gt_max_emax) self.assertRaises(TypeError, setattr, c, 'Emin', (1,2,3)) # Specific: rounding self.assertRaises(ValueError, setattr, c, 'rounding', -1) self.assertRaises(ValueError, setattr, c, 'rounding', 9) self.assertRaises(TypeError, setattr, c, 'rounding', 1.0) # Specific: capitals, clamp, _allcr for attr in ['capitals', 'clamp', '_allcr']: self.assertRaises(ValueError, setattr, c, attr, -1) self.assertRaises(ValueError, setattr, c, attr, 2) self.assertRaises(TypeError, setattr, c, attr, [1,2,3]) if HAVE_CONFIG_64: self.assertRaises(ValueError, setattr, c, attr, 232) self.assertRaises(ValueError, setattr, c, attr, 232+1) # Specific: _flags, _traps for attr in ['_flags', '_traps']: self.assertRaises(ValueError, setattr, c, attr, 999999) self.assertRaises(TypeError, setattr, c, attr, 'x') # SignalDict self.assertRaises(ValueError, c.flags.setitem, 801, 0) self.assertRaises(ValueError, c.traps.setitem, 801, 0) self.assertRaises(ValueError, c.flags.delitem, Overflow) self.assertRaises(ValueError, c.traps.delitem, InvalidOperation) self.assertRaises(TypeError, setattr, c, 'flags', ['x']) self.assertRaises(TypeError, setattr, c,'traps', ['y']) self.assertRaises(ValueError, setattr, c, 'flags', {0:1}) self.assertRaises(ValueError, setattr, c, 'traps', {0:1}) self.assertRaises(ValueError, c.setflags, ['x']) self.assertRaises(ValueError, c.settraps, ['y']) self.assertRaises(TypeError, c.setflags, 'x') self.assertRaises(TypeError, c.settraps, 'y') # SignalDict cannot be deleted self.assertRaises(ValueError, c.delattr, 'flags') self.assertRaises(ValueError, c.delattr, 'traps') # Invalid attributes self.assertRaises(TypeError, getattr, c, 9) self.assertRaises(TypeError, setattr, c, 9) # Invalid values in constructor self.assertRaises(ValueError, Context, prec=gt_max_emax) self.assertRaises(ValueError, Context, Emax=gt_max_emax) self.assertRaises(ValueError, Context, Emin=-gt_max_emax) self.assertRaises(ValueError, Context, rounding=999999) self.assertRaises(ValueError, Context, clamp=2) self.assertRaises(ValueError, Context, capitals=-1) self.assertRaises(ValueError, Context, flags=["P"]) self.assertRaises(ValueError, Context, traps=["Q"]) self.assertRaises(ValueError, Context, _allcr=2) # Overflow in conversion self.assertRaises(OverflowError, Context, prec=int_max+1) self.assertRaises(OverflowError, Context, Emax=int_max+1) self.assertRaises(OverflowError, Context, Emin=-int_max-2) self.assertRaises(OverflowError, Context, rounding=int_max+1) self.assertRaises(OverflowError, Context, clamp=int_max+1) self.assertRaises(OverflowError, Context, capitals=int_max+1) self.assertRaises(OverflowError, Context, _allcr=int_max+1) # Type error in conversion self.assertRaises(TypeError, Context, flags=(0,1)) self.assertRaises(TypeError, Context, traps=(1,0)) def test_context_repr(self): if not HAVE_CDECIMAL: return c = DefaultContext.copy() c.prec = 425000000 c.Emax = 425000000 c.Emin = -425000000 c.rounding = ROUND_HALF_DOWN c.capitals = 0 c.clamp = 1 for sig in OrderedSignals: c.flags[sig] = True c.traps[sig] = True c.flags[FloatOperation] = True c.traps[FloatOperation] = True s = c.repr() t = "Context(prec=425000000, rounding=ROUND_HALF_DOWN, " [](#l3003) "Emin=-425000000, Emax=425000000, capitals=0, clamp=1, " [](#l3004) "flags=Clamped, InvalidOperation, DivisionByZero, Inexact, " [ "FloatOperation, Overflow, Rounded, Subnormal, Underflow], " [](#l3006) "traps=Clamped, InvalidOperation, DivisionByZero, Inexact, " [ "FloatOperation, Overflow, Rounded, Subnormal, Underflow])" self.assertEqual(s, t) def test_valid_context(self): if not HAVE_CDECIMAL: return c = DefaultContext.copy() # Exercise all getters and setters c.prec = 34 c.rounding = ROUND_HALF_UP c.Emax = 3000 c.Emin = -3000 c.capitals = 1 c.clamp = 0 c._flags = DecUnderflow c._traps = DecClamped c._allcr = 0 self.assertEqual(c.prec, 34) self.assertEqual(c.rounding, ROUND_HALF_UP) self.assertEqual(c.Emin, -3000) self.assertEqual(c.Emax, 3000) self.assertEqual(c.capitals, 1) self.assertEqual(c.clamp, 0) self.assertEqual(c._flags, DecUnderflow) self.assertEqual(c._traps, DecClamped) self.assertEqual(c._allcr, 0) self.assertEqual(c.Etiny(), -3033) self.assertEqual(c.Etop(), 2967) # Set traps/flags from list c.settraps([Clamped, Underflow]) self.assertEqual(c._traps, DecClamped|DecUnderflow) c.setflags([Inexact, Rounded, Subnormal]) self.assertEqual(c._flags, DecInexact|DecRounded|DecSubnormal) # Exercise all unsafe setters c.unsafe_setprec(999999999) c.unsafe_setemax(999999999) c.unsafe_setemin(-999999999) self.assertEqual(c.prec, 999999999) self.assertEqual(c.Emax, 999999999) self.assertEqual(c.Emin, -999999999) def test_apply(self): if not HAVE_CDECIMAL: return with localcontext() as c: c.prec = 5 c.Emax = 99 c.Emin = -99 d = c.copy() d.prec = 4 x = Decimal("123456") self.assertEqual(str(x.apply()), "1.2346E+5") self.assertEqual(str(c.apply(x)), "1.2346E+5") self.assertEqual(str(x.apply(d)), "1.235E+5") self.assertEqual(str(d.apply(x)), "1.235E+5") self.assertRaises(TypeError, x.apply, "p") self.assertRaises(TypeError, x.apply, "p", "q") self.assertRaises(TypeError, c.apply, "p") def test_integral(self): if HAVE_CDECIMAL: x = Decimal(10) self.assertEqual(x.to_integral(), 10) self.assertRaises(TypeError, x.to_integral, '10') self.assertRaises(TypeError, x.to_integral, 10, 'x') self.assertRaises(ValueError, x.to_integral, 10) self.assertEqual(x.to_integral_value(), 10) self.assertRaises(TypeError, x.to_integral_value, '10') self.assertRaises(TypeError, x.to_integral_value, 10, 'x') self.assertRaises(ValueError, x.to_integral_value, 10) self.assertEqual(x.to_integral_exact(), 10) self.assertRaises(TypeError, x.to_integral_exact, '10') self.assertRaises(TypeError, x.to_integral_exact, 10, 'x') self.assertRaises(ValueError, x.to_integral_exact, 10) with localcontext() as c: x = Decimal("99999999999999999999999999.9").to_integral_value(ROUND_UP) self.assertEqual(x, Decimal('100000000000000000000000000')) x = Decimal("99999999999999999999999999.9").to_integral_exact(ROUND_UP) self.assertEqual(x, Decimal('100000000000000000000000000')) c.traps[Inexact] = True self.assertRaises(Inexact, Decimal("999.9").to_integral_exact, ROUND_UP) def test_round(self): # Python3 behavior: round() returns Decimal c = getcontext() c.prec = 28 int_max = 263-1 if HAVE_CONFIG_64 else 231-1 self.assertEqual(str(Decimal("9.99").round()), "10") self.assertEqual(str(Decimal("9.99e-5").round()), "0") self.assertEqual(str(Decimal("1.23456789").round(5)), "1.23457") self.assertEqual(str(Decimal("1.2345").round(10)), "1.2345000000") self.assertEqual(str(Decimal("1.2345").round(-10)), "0E+10") self.assertRaises(TypeError, Decimal("1.23").round, "5") self.assertRaises(TypeError, Decimal("1.23").round, 5, 8) if HAVE_CDECIMAL: with localcontext() as c: c.traps[InvalidOperation] = True self.assertRaises(InvalidOperation, Decimal("1.23").round, -int_max-1) self.assertRaises(InvalidOperation, Decimal("1.23").round, int_max) self.assertRaises(InvalidOperation, Decimal("1").round, int(MAX_EMAX+1)) self.assertRaises(InvalidOperation, Decimal("1").round, -int(MIN_ETINY-1)) self.assertRaises(OverflowError, Decimal("1.23").round, -int_max-2) self.assertRaises(OverflowError, Decimal("1.23").round, int_max+1) def test_format(self): if not HAVE_CDECIMAL: return self.assertRaises(TypeError, Decimal(1).format, "=10.10", {}, 9) self.assertRaises(TypeError, Decimal(1).format, "=10.10", 9) self.assertRaises(TypeError, Decimal(1).format, []) with localcontext() as c: c.traps[InvalidOperation] = True c.traps[Rounded] = True self.assertRaises(ValueError, Decimal(1).format, "<>=10.10") maxsize = 263-1 if HAVE_CONFIG_64 else 231-1 self.assertRaises(InvalidOperation, Decimal("1.23456789").format, "=%d.1" % maxsize) def test_funcs(self): if not HAVE_CDECIMAL: return # Invalid arguments self.assertRaises(TypeError, pow, Decimal(1), 2, "3") self.assertRaises(TypeError, Decimal(9).number_class, "x", "y") self.assertRaises(TypeError, Decimal(9).divmod, 8, "x", "y") self.assertRaises(TypeError, Decimal(9).same_quantum, 3, "x", "y") self.assertRaises(TypeError, Decimal(9).to_sci, 3, "x", "y") self.assertRaises(TypeError, Decimal(9).to_eng, 3, "x", "y") self.assertEqual(Decimal("1.234e2007").sign(), 1) self.assertEqual(Decimal("-1.234e2007").sign(), -1) with localcontext() as c: c.clear_traps() # Invalid arguments self.assertRaises(TypeError, c.copy_sign, Decimal(1), "x", "y") self.assertRaises(TypeError, c.canonical, 200) self.assertRaises(TypeError, c.is_canonical, 200) self.assertRaises(TypeError, c.divmod, 9, 8, "x", "y") self.assertRaises(TypeError, c.same_quantum, 9, 3, "x", "y") self.assertEqual(str(c.canonical(Decimal(200))), '200') self.assertEqual(c.radix(), 10) c.traps[DivisionByZero] = True self.assertRaises(DivisionByZero, Decimal(9).divmod, 0) self.assertRaises(DivisionByZero, Decimal(9).divmod, 0) self.assertRaises(DivisionByZero, c.divmod, 9, 0) self.assertTrue(c.flags[InvalidOperation]) c.clear_flags() c.traps[InvalidOperation] = True self.assertRaises(InvalidOperation, Decimal(9).divmod, 0) self.assertRaises(InvalidOperation, Decimal(9).divmod, 0) self.assertRaises(InvalidOperation, c.divmod, 9, 0) self.assertTrue(c.flags[DivisionByZero]) c.traps[InvalidOperation] = True c.prec = 2 self.assertRaises(InvalidOperation, pow, Decimal(1000), 1, 501) c.prec = 10 x = Decimal(2).invroot() self.assertEqual(str(x), '0.7071067812') x = c.invroot(3) self.assertEqual(str(x), '0.5773502692') c.prec = 28 x = Decimal(2).power(8) self.assertEqual(str(x), '256') x = Decimal(2).powmod(8, 31) self.assertEqual(str(x), '8') def test_signal_dict(self): if not HAVE_CDECIMAL: return import itertools def assertIsExclusivelySet(signal, signal_dict): for sig in signal_dict: if sig == signal: self.assertTrue(signal_dict[sig]) else: self.assertFalse(signal_dict[sig]) c = DefaultContext.copy() # Signal dict methods self.assertTrue(Overflow in c.traps) self.assertTrue(c.traps.has_key(Overflow)) c.clear_traps() for k in c.traps.keys(): c.traps[k] = True for v in c.traps.values(): self.assertTrue(v) c.clear_traps() for k, v in c.traps.items(): self.assertFalse(v) self.assertFalse(c.flags.get(Overflow)) self.assertIs(c.flags.get("x"), None) self.assertEqual(c.flags.get("x", "y"), "y") self.assertRaises(TypeError, c.flags.get, "x", "y", "z") self.assertEqual(len(c.flags), len(c.traps)) s = sys.getsizeof(c.flags) s = sys.getsizeof(c.traps) s = c.flags.repr() # Set flags/traps. c.clear_flags() c._flags = DecClamped self.assertTrue(c.flags[Clamped]) c.clear_traps() c._traps = DecInvalidOperation self.assertTrue(c.traps[InvalidOperation]) # Set flags/traps from dictionary. c.clear_flags() d = c.flags.copy() d[DivisionByZero] = True c.flags = d assertIsExclusivelySet(DivisionByZero, c.flags) c.clear_traps() d = c.traps.copy() d[Underflow] = True c.traps = d assertIsExclusivelySet(Underflow, c.traps) # Random constructors IntSignals = { Clamped: DecClamped, Rounded: DecRounded, Inexact: DecInexact, Subnormal: DecSubnormal, Underflow: DecUnderflow, Overflow: DecOverflow, DivisionByZero: DecDivisionByZero, InvalidOperation: DecIEEEInvalidOperation } IntCond = [ DecDivisionImpossible, DecDivisionUndefined, DecFpuError, DecInvalidContext, DecInvalidOperation, DecMallocError, DecConversionSyntax, ] for r in range(len(OrderedSignals)): for t in range(len(OrderedSignals)): for round in RoundingDict.values(): flags = random.sample(OrderedSignals, r) traps = random.sample(OrderedSignals, t) prec = random.randrange(1, 10000) emin = random.randrange(-10000, 0) emax = random.randrange(0, 10000) clamp = random.randrange(0, 2) caps = random.randrange(0, 2) cr = random.randrange(0, 2) c = Context(prec=prec, rounding=round, Emin=emin, Emax=emax, capitals=caps, clamp=clamp, flags=list(flags), traps=list(traps), _allcr=cr) self.assertEqual(c.prec, prec) self.assertEqual(c.rounding, round) self.assertEqual(c.Emin, emin) self.assertEqual(c.Emax, emax) self.assertEqual(c.capitals, caps) self.assertEqual(c.clamp, clamp) self.assertEqual(c._allcr, cr) f = 0 for x in flags: f |= IntSignals[x] self.assertEqual(c._flags, f) f = 0 for x in traps: f |= IntSignals[x] self.assertEqual(c._traps, f) for cond in IntCond: c._flags = cond self.assertTrue(c._flags&DecIEEEInvalidOperation) assertIsExclusivelySet(InvalidOperation, c.flags) for cond in IntCond: c._traps = cond self.assertTrue(c._traps&DecIEEEInvalidOperation) assertIsExclusivelySet(InvalidOperation, c.traps) def test_bignum(self): b1 = 1035 b2 = 10**36 with localcontext() as c: c.prec = 1000000 for i in range(5): a = random.randrange(b1, b2) b = random.randrange(1000, 1200) x = a ** b y = Decimal(a) ** Decimal(b) self.assertEqual(x, y) def test_main(arith=False, verbose=None, todo_tests=None, debug=None): """ Execute the tests. Runs all arithmetic tests if arith is True or if the "decimal" resource is enabled in regrtest.py """ init() global TEST_ALL, DEBUG TEST_ALL = arith or is_resource_enabled('decimal') DEBUG = debug if todo_tests is None: test_classes = [ DecimalExplicitConstructionTest, DecimalImplicitConstructionTest, DecimalArithmeticOperatorsTest, DecimalFormatTest, DecimalUseOfContextTest, DecimalUsabilityTest, DecimalPythonAPItests, ContextAPItests, DecimalTest, WithStatementTest, ContextFlags, SpecialContexts, Coverage ] else: test_classes = [DecimalTest] # Dynamically build custom test definition for each file in the test # directory and add the definitions to the DecimalTest class. This # procedure insures that new files do not get skipped. for filename in os.listdir(directory): if '.decTest' not in filename or filename.startswith("."): continue head, tail = filename.split('.') if todo_tests is not None and head not in todo_tests: continue tester = lambda self, f=filename: self.eval_file(directory + f) setattr(DecimalTest, 'test_' + head, tester) del filename, head, tail, tester try: run_unittest(*test_classes) if todo_tests is None: if HAVE_CDECIMAL: import cdecimal as DecimalModule else: import decimal as DecimalModule run_doctest(DecimalModule, verbose) finally: setcontext(ORIGINAL_CONTEXT) if name == 'main': import optparse p = optparse.OptionParser("test_decimal.py [--debug] [{--skip | test1 [test2 [...]]}]") p.add_option('--debug', '-d', action='store_true', help='shows the test number and context before each test') p.add_option('--skip', '-s', action='store_true', help='skip over 90% of the arithmetic tests') (opt, args) = p.parse_args() if opt.skip: test_main(arith=False, verbose=True) elif args: test_main(arith=True, verbose=True, todo_tests=args, debug=opt.debug) else: test_main(arith=True, verbose=True)