Issue #6850: Fix bug in Decimal._parse_format_specifier for formats
[python.git] / Lib / test / test_decimal.py
blobf7f023d22d0455ab5847d563098021d584380834
1 # Copyright (c) 2004 Python Software Foundation.
2 # All rights reserved.
4 # Written by Eric Price <eprice at tjhsst.edu>
5 # and Facundo Batista <facundo at taniquetil.com.ar>
6 # and Raymond Hettinger <python at rcn.com>
7 # and Aahz (aahz at pobox.com)
8 # and Tim Peters
10 """
11 These are the test cases for the Decimal module.
13 There are two groups of tests, Arithmetic and Behaviour. The former test
14 the Decimal arithmetic using the tests provided by Mike Cowlishaw. The latter
15 test the pythonic behaviour according to PEP 327.
17 Cowlishaw's tests can be downloaded from:
19 www2.hursley.ibm.com/decimal/dectest.zip
21 This test module can be called from command line with one parameter (Arithmetic
22 or Behaviour) to test each part, or without parameter to test both parts. If
23 you're working through IDLE, you can import this test module and call test_main()
24 with the corresponding argument.
25 """
27 import glob
28 import math
29 import os, sys
30 import pickle, copy
31 import unittest
32 from decimal import *
33 import numbers
34 from test.test_support import (run_unittest, run_doctest, is_resource_enabled)
35 import random
36 try:
37 import threading
38 except ImportError:
39 threading = None
41 # Useful Test Constant
42 Signals = getcontext().flags.keys()
44 # Tests are built around these assumed context defaults.
45 # test_main() restores the original context.
46 def init():
47 global ORIGINAL_CONTEXT
48 ORIGINAL_CONTEXT = getcontext().copy()
49 DefaultTestContext = Context(
50 prec = 9,
51 rounding = ROUND_HALF_EVEN,
52 traps = dict.fromkeys(Signals, 0)
54 setcontext(DefaultTestContext)
56 TESTDATADIR = 'decimaltestdata'
57 if __name__ == '__main__':
58 file = sys.argv[0]
59 else:
60 file = __file__
61 testdir = os.path.dirname(file) or os.curdir
62 directory = testdir + os.sep + TESTDATADIR + os.sep
64 skip_expected = not os.path.isdir(directory)
66 # Make sure it actually raises errors when not expected and caught in flags
67 # Slower, since it runs some things several times.
68 EXTENDEDERRORTEST = False
70 #Map the test cases' error names to the actual errors
71 ErrorNames = {'clamped' : Clamped,
72 'conversion_syntax' : InvalidOperation,
73 'division_by_zero' : DivisionByZero,
74 'division_impossible' : InvalidOperation,
75 'division_undefined' : InvalidOperation,
76 'inexact' : Inexact,
77 'invalid_context' : InvalidOperation,
78 'invalid_operation' : InvalidOperation,
79 'overflow' : Overflow,
80 'rounded' : Rounded,
81 'subnormal' : Subnormal,
82 'underflow' : Underflow}
85 def Nonfunction(*args):
86 """Doesn't do anything."""
87 return None
89 RoundingDict = {'ceiling' : ROUND_CEILING, #Maps test-case names to roundings.
90 'down' : ROUND_DOWN,
91 'floor' : ROUND_FLOOR,
92 'half_down' : ROUND_HALF_DOWN,
93 'half_even' : ROUND_HALF_EVEN,
94 'half_up' : ROUND_HALF_UP,
95 'up' : ROUND_UP,
96 '05up' : ROUND_05UP}
98 # Name adapter to be able to change the Decimal and Context
99 # interface without changing the test files from Cowlishaw
100 nameAdapter = {'and':'logical_and',
101 'apply':'_apply',
102 'class':'number_class',
103 'comparesig':'compare_signal',
104 'comparetotal':'compare_total',
105 'comparetotmag':'compare_total_mag',
106 'copy':'copy_decimal',
107 'copyabs':'copy_abs',
108 'copynegate':'copy_negate',
109 'copysign':'copy_sign',
110 'divideint':'divide_int',
111 'invert':'logical_invert',
112 'iscanonical':'is_canonical',
113 'isfinite':'is_finite',
114 'isinfinite':'is_infinite',
115 'isnan':'is_nan',
116 'isnormal':'is_normal',
117 'isqnan':'is_qnan',
118 'issigned':'is_signed',
119 'issnan':'is_snan',
120 'issubnormal':'is_subnormal',
121 'iszero':'is_zero',
122 'maxmag':'max_mag',
123 'minmag':'min_mag',
124 'nextminus':'next_minus',
125 'nextplus':'next_plus',
126 'nexttoward':'next_toward',
127 'or':'logical_or',
128 'reduce':'normalize',
129 'remaindernear':'remainder_near',
130 'samequantum':'same_quantum',
131 'squareroot':'sqrt',
132 'toeng':'to_eng_string',
133 'tointegral':'to_integral_value',
134 'tointegralx':'to_integral_exact',
135 'tosci':'to_sci_string',
136 'xor':'logical_xor',
139 # The following functions return True/False rather than a Decimal instance
141 LOGICAL_FUNCTIONS = (
142 'is_canonical',
143 'is_finite',
144 'is_infinite',
145 'is_nan',
146 'is_normal',
147 'is_qnan',
148 'is_signed',
149 'is_snan',
150 'is_subnormal',
151 'is_zero',
152 'same_quantum',
155 # For some operations (currently exp, ln, log10, power), the decNumber
156 # reference implementation imposes additional restrictions on the
157 # context and operands. These restrictions are not part of the
158 # specification; however, the effect of these restrictions does show
159 # up in some of the testcases. We skip testcases that violate these
160 # restrictions, since Decimal behaves differently from decNumber for
161 # these testcases so these testcases would otherwise fail.
163 decNumberRestricted = ('power', 'ln', 'log10', 'exp')
164 DEC_MAX_MATH = 999999
165 def outside_decNumber_bounds(v, context):
166 if (context.prec > DEC_MAX_MATH or
167 context.Emax > DEC_MAX_MATH or
168 -context.Emin > DEC_MAX_MATH):
169 return True
170 if not v._is_special and v and (
171 v.adjusted() > DEC_MAX_MATH or
172 v.adjusted() < 1-2*DEC_MAX_MATH):
173 return True
174 return False
176 class DecimalTest(unittest.TestCase):
177 """Class which tests the Decimal class against the test cases.
179 Changed for unittest.
181 def setUp(self):
182 self.context = Context()
183 self.ignore_list = ['#']
184 # Basically, a # means return NaN InvalidOperation.
185 # Different from a sNaN in trim
187 self.ChangeDict = {'precision' : self.change_precision,
188 'rounding' : self.change_rounding_method,
189 'maxexponent' : self.change_max_exponent,
190 'minexponent' : self.change_min_exponent,
191 'clamp' : self.change_clamp}
193 def eval_file(self, file):
194 global skip_expected
195 if skip_expected:
196 raise unittest.SkipTest
197 return
198 for line in open(file).xreadlines():
199 line = line.replace('\r\n', '').replace('\n', '')
200 #print line
201 try:
202 t = self.eval_line(line)
203 except DecimalException, exception:
204 #Exception raised where there shoudn't have been one.
205 self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line)
207 return
209 def eval_line(self, s):
210 if s.find(' -> ') >= 0 and s[:2] != '--' and not s.startswith(' --'):
211 s = (s.split('->')[0] + '->' +
212 s.split('->')[1].split('--')[0]).strip()
213 else:
214 s = s.split('--')[0].strip()
216 for ignore in self.ignore_list:
217 if s.find(ignore) >= 0:
218 #print s.split()[0], 'NotImplemented--', ignore
219 return
220 if not s:
221 return
222 elif ':' in s:
223 return self.eval_directive(s)
224 else:
225 return self.eval_equation(s)
227 def eval_directive(self, s):
228 funct, value = map(lambda x: x.strip().lower(), s.split(':'))
229 if funct == 'rounding':
230 value = RoundingDict[value]
231 else:
232 try:
233 value = int(value)
234 except ValueError:
235 pass
237 funct = self.ChangeDict.get(funct, Nonfunction)
238 funct(value)
240 def eval_equation(self, s):
241 #global DEFAULT_PRECISION
242 #print DEFAULT_PRECISION
244 if not TEST_ALL and random.random() < 0.90:
245 return
247 try:
248 Sides = s.split('->')
249 L = Sides[0].strip().split()
250 id = L[0]
251 if DEBUG:
252 print "Test ", id,
253 funct = L[1].lower()
254 valstemp = L[2:]
255 L = Sides[1].strip().split()
256 ans = L[0]
257 exceptions = L[1:]
258 except (TypeError, AttributeError, IndexError):
259 raise InvalidOperation
260 def FixQuotes(val):
261 val = val.replace("''", 'SingleQuote').replace('""', 'DoubleQuote')
262 val = val.replace("'", '').replace('"', '')
263 val = val.replace('SingleQuote', "'").replace('DoubleQuote', '"')
264 return val
265 fname = nameAdapter.get(funct, funct)
266 if fname == 'rescale':
267 return
268 funct = getattr(self.context, fname)
269 vals = []
270 conglomerate = ''
271 quote = 0
272 theirexceptions = [ErrorNames[x.lower()] for x in exceptions]
274 for exception in Signals:
275 self.context.traps[exception] = 1 #Catch these bugs...
276 for exception in theirexceptions:
277 self.context.traps[exception] = 0
278 for i, val in enumerate(valstemp):
279 if val.count("'") % 2 == 1:
280 quote = 1 - quote
281 if quote:
282 conglomerate = conglomerate + ' ' + val
283 continue
284 else:
285 val = conglomerate + val
286 conglomerate = ''
287 v = FixQuotes(val)
288 if fname in ('to_sci_string', 'to_eng_string'):
289 if EXTENDEDERRORTEST:
290 for error in theirexceptions:
291 self.context.traps[error] = 1
292 try:
293 funct(self.context.create_decimal(v))
294 except error:
295 pass
296 except Signals, e:
297 self.fail("Raised %s in %s when %s disabled" % \
298 (e, s, error))
299 else:
300 self.fail("Did not raise %s in %s" % (error, s))
301 self.context.traps[error] = 0
302 v = self.context.create_decimal(v)
303 else:
304 v = Decimal(v, self.context)
305 vals.append(v)
307 ans = FixQuotes(ans)
309 # skip tests that are related to bounds imposed in the decNumber
310 # reference implementation
311 if fname in decNumberRestricted:
312 if fname == 'power':
313 if not (vals[1]._isinteger() and
314 -1999999997 <= vals[1] <= 999999999):
315 if outside_decNumber_bounds(vals[0], self.context) or \
316 outside_decNumber_bounds(vals[1], self.context):
317 #print "Skipping test %s" % s
318 return
319 else:
320 if outside_decNumber_bounds(vals[0], self.context):
321 #print "Skipping test %s" % s
322 return
325 if EXTENDEDERRORTEST and fname not in ('to_sci_string', 'to_eng_string'):
326 for error in theirexceptions:
327 self.context.traps[error] = 1
328 try:
329 funct(*vals)
330 except error:
331 pass
332 except Signals, e:
333 self.fail("Raised %s in %s when %s disabled" % \
334 (e, s, error))
335 else:
336 self.fail("Did not raise %s in %s" % (error, s))
337 self.context.traps[error] = 0
338 if DEBUG:
339 print "--", self.context
340 try:
341 result = str(funct(*vals))
342 if fname in LOGICAL_FUNCTIONS:
343 result = str(int(eval(result))) # 'True', 'False' -> '1', '0'
344 except Signals, error:
345 self.fail("Raised %s in %s" % (error, s))
346 except: #Catch any error long enough to state the test case.
347 print "ERROR:", s
348 raise
350 myexceptions = self.getexceptions()
351 self.context.clear_flags()
353 myexceptions.sort()
354 theirexceptions.sort()
356 self.assertEqual(result, ans,
357 'Incorrect answer for ' + s + ' -- got ' + result)
358 self.assertEqual(myexceptions, theirexceptions,
359 'Incorrect flags set in ' + s + ' -- got ' + str(myexceptions))
360 return
362 def getexceptions(self):
363 return [e for e in Signals if self.context.flags[e]]
365 def change_precision(self, prec):
366 self.context.prec = prec
367 def change_rounding_method(self, rounding):
368 self.context.rounding = rounding
369 def change_min_exponent(self, exp):
370 self.context.Emin = exp
371 def change_max_exponent(self, exp):
372 self.context.Emax = exp
373 def change_clamp(self, clamp):
374 self.context._clamp = clamp
378 # The following classes test the behaviour of Decimal according to PEP 327
380 class DecimalExplicitConstructionTest(unittest.TestCase):
381 '''Unit tests for Explicit Construction cases of Decimal.'''
383 def test_explicit_empty(self):
384 self.assertEqual(Decimal(), Decimal("0"))
386 def test_explicit_from_None(self):
387 self.assertRaises(TypeError, Decimal, None)
389 def test_explicit_from_int(self):
391 #positive
392 d = Decimal(45)
393 self.assertEqual(str(d), '45')
395 #very large positive
396 d = Decimal(500000123)
397 self.assertEqual(str(d), '500000123')
399 #negative
400 d = Decimal(-45)
401 self.assertEqual(str(d), '-45')
403 #zero
404 d = Decimal(0)
405 self.assertEqual(str(d), '0')
407 def test_explicit_from_string(self):
409 #empty
410 self.assertEqual(str(Decimal('')), 'NaN')
412 #int
413 self.assertEqual(str(Decimal('45')), '45')
415 #float
416 self.assertEqual(str(Decimal('45.34')), '45.34')
418 #engineer notation
419 self.assertEqual(str(Decimal('45e2')), '4.5E+3')
421 #just not a number
422 self.assertEqual(str(Decimal('ugly')), 'NaN')
424 #leading and trailing whitespace permitted
425 self.assertEqual(str(Decimal('1.3E4 \n')), '1.3E+4')
426 self.assertEqual(str(Decimal(' -7.89')), '-7.89')
428 #unicode strings should be permitted
429 self.assertEqual(str(Decimal(u'0E-017')), '0E-17')
430 self.assertEqual(str(Decimal(u'45')), '45')
431 self.assertEqual(str(Decimal(u'-Inf')), '-Infinity')
432 self.assertEqual(str(Decimal(u'NaN123')), 'NaN123')
434 def test_explicit_from_tuples(self):
436 #zero
437 d = Decimal( (0, (0,), 0) )
438 self.assertEqual(str(d), '0')
440 #int
441 d = Decimal( (1, (4, 5), 0) )
442 self.assertEqual(str(d), '-45')
444 #float
445 d = Decimal( (0, (4, 5, 3, 4), -2) )
446 self.assertEqual(str(d), '45.34')
448 #weird
449 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
450 self.assertEqual(str(d), '-4.34913534E-17')
452 #wrong number of items
453 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) )
455 #bad sign
456 self.assertRaises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) )
457 self.assertRaises(ValueError, Decimal, (0., (4, 3, 4, 9, 1), 2) )
458 self.assertRaises(ValueError, Decimal, (Decimal(1), (4, 3, 4, 9, 1), 2))
460 #bad exp
461 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') )
462 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 0.) )
463 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), '1') )
465 #bad coefficients
466 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
467 self.assertRaises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
468 self.assertRaises(ValueError, Decimal, (1, (4, 10, 4, 9, 1), 2) )
469 self.assertRaises(ValueError, Decimal, (1, (4, 3, 4, 'a', 1), 2) )
471 def test_explicit_from_Decimal(self):
473 #positive
474 d = Decimal(45)
475 e = Decimal(d)
476 self.assertEqual(str(e), '45')
477 self.assertNotEqual(id(d), id(e))
479 #very large positive
480 d = Decimal(500000123)
481 e = Decimal(d)
482 self.assertEqual(str(e), '500000123')
483 self.assertNotEqual(id(d), id(e))
485 #negative
486 d = Decimal(-45)
487 e = Decimal(d)
488 self.assertEqual(str(e), '-45')
489 self.assertNotEqual(id(d), id(e))
491 #zero
492 d = Decimal(0)
493 e = Decimal(d)
494 self.assertEqual(str(e), '0')
495 self.assertNotEqual(id(d), id(e))
497 def test_explicit_context_create_decimal(self):
499 nc = copy.copy(getcontext())
500 nc.prec = 3
502 # empty
503 d = Decimal()
504 self.assertEqual(str(d), '0')
505 d = nc.create_decimal()
506 self.assertEqual(str(d), '0')
508 # from None
509 self.assertRaises(TypeError, nc.create_decimal, None)
511 # from int
512 d = nc.create_decimal(456)
513 self.assertTrue(isinstance(d, Decimal))
514 self.assertEqual(nc.create_decimal(45678),
515 nc.create_decimal('457E+2'))
517 # from string
518 d = Decimal('456789')
519 self.assertEqual(str(d), '456789')
520 d = nc.create_decimal('456789')
521 self.assertEqual(str(d), '4.57E+5')
522 # leading and trailing whitespace should result in a NaN;
523 # spaces are already checked in Cowlishaw's test-suite, so
524 # here we just check that a trailing newline results in a NaN
525 self.assertEqual(str(nc.create_decimal('3.14\n')), 'NaN')
527 # from tuples
528 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
529 self.assertEqual(str(d), '-4.34913534E-17')
530 d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
531 self.assertEqual(str(d), '-4.35E-17')
533 # from Decimal
534 prevdec = Decimal(500000123)
535 d = Decimal(prevdec)
536 self.assertEqual(str(d), '500000123')
537 d = nc.create_decimal(prevdec)
538 self.assertEqual(str(d), '5.00E+8')
540 def test_unicode_digits(self):
541 test_values = {
542 u'\uff11': '1',
543 u'\u0660.\u0660\u0663\u0667\u0662e-\u0663' : '0.0000372',
544 u'-nan\u0c68\u0c6a\u0c66\u0c66' : '-NaN2400',
546 for input, expected in test_values.items():
547 self.assertEqual(str(Decimal(input)), expected)
550 class DecimalImplicitConstructionTest(unittest.TestCase):
551 '''Unit tests for Implicit Construction cases of Decimal.'''
553 def test_implicit_from_None(self):
554 self.assertRaises(TypeError, eval, 'Decimal(5) + None', globals())
556 def test_implicit_from_int(self):
557 #normal
558 self.assertEqual(str(Decimal(5) + 45), '50')
559 #exceeding precision
560 self.assertEqual(Decimal(5) + 123456789000, Decimal(123456789000))
562 def test_implicit_from_string(self):
563 self.assertRaises(TypeError, eval, 'Decimal(5) + "3"', globals())
565 def test_implicit_from_float(self):
566 self.assertRaises(TypeError, eval, 'Decimal(5) + 2.2', globals())
568 def test_implicit_from_Decimal(self):
569 self.assertEqual(Decimal(5) + Decimal(45), Decimal(50))
571 def test_rop(self):
572 # Allow other classes to be trained to interact with Decimals
573 class E:
574 def __divmod__(self, other):
575 return 'divmod ' + str(other)
576 def __rdivmod__(self, other):
577 return str(other) + ' rdivmod'
578 def __lt__(self, other):
579 return 'lt ' + str(other)
580 def __gt__(self, other):
581 return 'gt ' + str(other)
582 def __le__(self, other):
583 return 'le ' + str(other)
584 def __ge__(self, other):
585 return 'ge ' + str(other)
586 def __eq__(self, other):
587 return 'eq ' + str(other)
588 def __ne__(self, other):
589 return 'ne ' + str(other)
591 self.assertEqual(divmod(E(), Decimal(10)), 'divmod 10')
592 self.assertEqual(divmod(Decimal(10), E()), '10 rdivmod')
593 self.assertEqual(eval('Decimal(10) < E()'), 'gt 10')
594 self.assertEqual(eval('Decimal(10) > E()'), 'lt 10')
595 self.assertEqual(eval('Decimal(10) <= E()'), 'ge 10')
596 self.assertEqual(eval('Decimal(10) >= E()'), 'le 10')
597 self.assertEqual(eval('Decimal(10) == E()'), 'eq 10')
598 self.assertEqual(eval('Decimal(10) != E()'), 'ne 10')
600 # insert operator methods and then exercise them
601 oplist = [
602 ('+', '__add__', '__radd__'),
603 ('-', '__sub__', '__rsub__'),
604 ('*', '__mul__', '__rmul__'),
605 ('%', '__mod__', '__rmod__'),
606 ('//', '__floordiv__', '__rfloordiv__'),
607 ('**', '__pow__', '__rpow__')
609 if 1/2 == 0:
610 # testing with classic division, so add __div__
611 oplist.append(('/', '__div__', '__rdiv__'))
612 else:
613 # testing with -Qnew, so add __truediv__
614 oplist.append(('/', '__truediv__', '__rtruediv__'))
616 for sym, lop, rop in oplist:
617 setattr(E, lop, lambda self, other: 'str' + lop + str(other))
618 setattr(E, rop, lambda self, other: str(other) + rop + 'str')
619 self.assertEqual(eval('E()' + sym + 'Decimal(10)'),
620 'str' + lop + '10')
621 self.assertEqual(eval('Decimal(10)' + sym + 'E()'),
622 '10' + rop + 'str')
625 class DecimalFormatTest(unittest.TestCase):
626 '''Unit tests for the format function.'''
627 def test_formatting(self):
628 # triples giving a format, a Decimal, and the expected result
629 test_values = [
630 ('e', '0E-15', '0e-15'),
631 ('e', '2.3E-15', '2.3e-15'),
632 ('e', '2.30E+2', '2.30e+2'), # preserve significant zeros
633 ('e', '2.30000E-15', '2.30000e-15'),
634 ('e', '1.23456789123456789e40', '1.23456789123456789e+40'),
635 ('e', '1.5', '1.5e+0'),
636 ('e', '0.15', '1.5e-1'),
637 ('e', '0.015', '1.5e-2'),
638 ('e', '0.0000000000015', '1.5e-12'),
639 ('e', '15.0', '1.50e+1'),
640 ('e', '-15', '-1.5e+1'),
641 ('e', '0', '0e+0'),
642 ('e', '0E1', '0e+1'),
643 ('e', '0.0', '0e-1'),
644 ('e', '0.00', '0e-2'),
645 ('.6e', '0E-15', '0.000000e-9'),
646 ('.6e', '0', '0.000000e+6'),
647 ('.6e', '9.999999', '9.999999e+0'),
648 ('.6e', '9.9999999', '1.000000e+1'),
649 ('.6e', '-1.23e5', '-1.230000e+5'),
650 ('.6e', '1.23456789e-3', '1.234568e-3'),
651 ('f', '0', '0'),
652 ('f', '0.0', '0.0'),
653 ('f', '0E-2', '0.00'),
654 ('f', '0.00E-8', '0.0000000000'),
655 ('f', '0E1', '0'), # loses exponent information
656 ('f', '3.2E1', '32'),
657 ('f', '3.2E2', '320'),
658 ('f', '3.20E2', '320'),
659 ('f', '3.200E2', '320.0'),
660 ('f', '3.2E-6', '0.0000032'),
661 ('.6f', '0E-15', '0.000000'), # all zeros treated equally
662 ('.6f', '0E1', '0.000000'),
663 ('.6f', '0', '0.000000'),
664 ('.0f', '0', '0'), # no decimal point
665 ('.0f', '0e-2', '0'),
666 ('.0f', '3.14159265', '3'),
667 ('.1f', '3.14159265', '3.1'),
668 ('.4f', '3.14159265', '3.1416'),
669 ('.6f', '3.14159265', '3.141593'),
670 ('.7f', '3.14159265', '3.1415926'), # round-half-even!
671 ('.8f', '3.14159265', '3.14159265'),
672 ('.9f', '3.14159265', '3.141592650'),
674 ('g', '0', '0'),
675 ('g', '0.0', '0.0'),
676 ('g', '0E1', '0e+1'),
677 ('G', '0E1', '0E+1'),
678 ('g', '0E-5', '0.00000'),
679 ('g', '0E-6', '0.000000'),
680 ('g', '0E-7', '0e-7'),
681 ('g', '-0E2', '-0e+2'),
682 ('.0g', '3.14159265', '3'), # 0 sig fig -> 1 sig fig
683 ('.1g', '3.14159265', '3'),
684 ('.2g', '3.14159265', '3.1'),
685 ('.5g', '3.14159265', '3.1416'),
686 ('.7g', '3.14159265', '3.141593'),
687 ('.8g', '3.14159265', '3.1415926'), # round-half-even!
688 ('.9g', '3.14159265', '3.14159265'),
689 ('.10g', '3.14159265', '3.14159265'), # don't pad
691 ('%', '0E1', '0%'),
692 ('%', '0E0', '0%'),
693 ('%', '0E-1', '0%'),
694 ('%', '0E-2', '0%'),
695 ('%', '0E-3', '0.0%'),
696 ('%', '0E-4', '0.00%'),
698 ('.3%', '0', '0.000%'), # all zeros treated equally
699 ('.3%', '0E10', '0.000%'),
700 ('.3%', '0E-10', '0.000%'),
701 ('.3%', '2.34', '234.000%'),
702 ('.3%', '1.234567', '123.457%'),
703 ('.0%', '1.23', '123%'),
705 ('e', 'NaN', 'NaN'),
706 ('f', '-NaN123', '-NaN123'),
707 ('+g', 'NaN456', '+NaN456'),
708 ('.3e', 'Inf', 'Infinity'),
709 ('.16f', '-Inf', '-Infinity'),
710 ('.0g', '-sNaN', '-sNaN'),
712 ('', '1.00', '1.00'),
714 # test alignment and padding
715 ('<6', '123', '123 '),
716 ('>6', '123', ' 123'),
717 ('^6', '123', ' 123 '),
718 ('=+6', '123', '+ 123'),
719 ('#<10', 'NaN', 'NaN#######'),
720 ('#<10', '-4.3', '-4.3######'),
721 ('#<+10', '0.0130', '+0.0130###'),
722 ('#< 10', '0.0130', ' 0.0130###'),
723 ('@>10', '-Inf', '@-Infinity'),
724 ('#>5', '-Inf', '-Infinity'),
725 ('?^5', '123', '?123?'),
726 ('%^6', '123', '%123%%'),
727 (' ^6', '-45.6', '-45.6 '),
728 ('/=10', '-45.6', '-/////45.6'),
729 ('/=+10', '45.6', '+/////45.6'),
730 ('/= 10', '45.6', ' /////45.6'),
732 # thousands separator
733 (',', '1234567', '1,234,567'),
734 (',', '123456', '123,456'),
735 (',', '12345', '12,345'),
736 (',', '1234', '1,234'),
737 (',', '123', '123'),
738 (',', '12', '12'),
739 (',', '1', '1'),
740 (',', '0', '0'),
741 (',', '-1234567', '-1,234,567'),
742 (',', '-123456', '-123,456'),
743 ('7,', '123456', '123,456'),
744 ('8,', '123456', '123,456 '),
745 ('08,', '123456', '0,123,456'), # special case: extra 0 needed
746 ('+08,', '123456', '+123,456'), # but not if there's a sign
747 (' 08,', '123456', ' 123,456'),
748 ('08,', '-123456', '-123,456'),
749 ('+09,', '123456', '+0,123,456'),
750 # ... with fractional part...
751 ('07,', '1234.56', '1,234.56'),
752 ('08,', '1234.56', '1,234.56'),
753 ('09,', '1234.56', '01,234.56'),
754 ('010,', '1234.56', '001,234.56'),
755 ('011,', '1234.56', '0,001,234.56'),
756 ('012,', '1234.56', '0,001,234.56'),
757 ('08,.1f', '1234.5', '01,234.5'),
758 # no thousands separators in fraction part
759 (',', '1.23456789', '1.23456789'),
760 (',%', '123.456789', '12,345.6789%'),
761 (',e', '123456', '1.23456e+5'),
762 (',E', '123456', '1.23456E+5'),
764 # issue 6850
765 ('a=-7.0', '0.12345', 'aaaa0.1'),
767 for fmt, d, result in test_values:
768 self.assertEqual(format(Decimal(d), fmt), result)
770 def test_n_format(self):
771 try:
772 from locale import CHAR_MAX
773 except ImportError:
774 return
776 # Set up some localeconv-like dictionaries
777 en_US = {
778 'decimal_point' : '.',
779 'grouping' : [3, 3, 0],
780 'thousands_sep': ','
783 fr_FR = {
784 'decimal_point' : ',',
785 'grouping' : [CHAR_MAX],
786 'thousands_sep' : ''
789 ru_RU = {
790 'decimal_point' : ',',
791 'grouping' : [3, 3, 0],
792 'thousands_sep' : ' '
795 crazy = {
796 'decimal_point' : '&',
797 'grouping' : [1, 4, 2, CHAR_MAX],
798 'thousands_sep' : '-'
802 def get_fmt(x, locale, fmt='n'):
803 return Decimal.__format__(Decimal(x), fmt, _localeconv=locale)
805 self.assertEqual(get_fmt(Decimal('12.7'), en_US), '12.7')
806 self.assertEqual(get_fmt(Decimal('12.7'), fr_FR), '12,7')
807 self.assertEqual(get_fmt(Decimal('12.7'), ru_RU), '12,7')
808 self.assertEqual(get_fmt(Decimal('12.7'), crazy), '1-2&7')
810 self.assertEqual(get_fmt(123456789, en_US), '123,456,789')
811 self.assertEqual(get_fmt(123456789, fr_FR), '123456789')
812 self.assertEqual(get_fmt(123456789, ru_RU), '123 456 789')
813 self.assertEqual(get_fmt(1234567890123, crazy), '123456-78-9012-3')
815 self.assertEqual(get_fmt(123456789, en_US, '.6n'), '1.23457e+8')
816 self.assertEqual(get_fmt(123456789, fr_FR, '.6n'), '1,23457e+8')
817 self.assertEqual(get_fmt(123456789, ru_RU, '.6n'), '1,23457e+8')
818 self.assertEqual(get_fmt(123456789, crazy, '.6n'), '1&23457e+8')
820 # zero padding
821 self.assertEqual(get_fmt(1234, fr_FR, '03n'), '1234')
822 self.assertEqual(get_fmt(1234, fr_FR, '04n'), '1234')
823 self.assertEqual(get_fmt(1234, fr_FR, '05n'), '01234')
824 self.assertEqual(get_fmt(1234, fr_FR, '06n'), '001234')
826 self.assertEqual(get_fmt(12345, en_US, '05n'), '12,345')
827 self.assertEqual(get_fmt(12345, en_US, '06n'), '12,345')
828 self.assertEqual(get_fmt(12345, en_US, '07n'), '012,345')
829 self.assertEqual(get_fmt(12345, en_US, '08n'), '0,012,345')
830 self.assertEqual(get_fmt(12345, en_US, '09n'), '0,012,345')
831 self.assertEqual(get_fmt(12345, en_US, '010n'), '00,012,345')
833 self.assertEqual(get_fmt(123456, crazy, '06n'), '1-2345-6')
834 self.assertEqual(get_fmt(123456, crazy, '07n'), '1-2345-6')
835 self.assertEqual(get_fmt(123456, crazy, '08n'), '1-2345-6')
836 self.assertEqual(get_fmt(123456, crazy, '09n'), '01-2345-6')
837 self.assertEqual(get_fmt(123456, crazy, '010n'), '0-01-2345-6')
838 self.assertEqual(get_fmt(123456, crazy, '011n'), '0-01-2345-6')
839 self.assertEqual(get_fmt(123456, crazy, '012n'), '00-01-2345-6')
840 self.assertEqual(get_fmt(123456, crazy, '013n'), '000-01-2345-6')
843 class DecimalArithmeticOperatorsTest(unittest.TestCase):
844 '''Unit tests for all arithmetic operators, binary and unary.'''
846 def test_addition(self):
848 d1 = Decimal('-11.1')
849 d2 = Decimal('22.2')
851 #two Decimals
852 self.assertEqual(d1+d2, Decimal('11.1'))
853 self.assertEqual(d2+d1, Decimal('11.1'))
855 #with other type, left
856 c = d1 + 5
857 self.assertEqual(c, Decimal('-6.1'))
858 self.assertEqual(type(c), type(d1))
860 #with other type, right
861 c = 5 + d1
862 self.assertEqual(c, Decimal('-6.1'))
863 self.assertEqual(type(c), type(d1))
865 #inline with decimal
866 d1 += d2
867 self.assertEqual(d1, Decimal('11.1'))
869 #inline with other type
870 d1 += 5
871 self.assertEqual(d1, Decimal('16.1'))
873 def test_subtraction(self):
875 d1 = Decimal('-11.1')
876 d2 = Decimal('22.2')
878 #two Decimals
879 self.assertEqual(d1-d2, Decimal('-33.3'))
880 self.assertEqual(d2-d1, Decimal('33.3'))
882 #with other type, left
883 c = d1 - 5
884 self.assertEqual(c, Decimal('-16.1'))
885 self.assertEqual(type(c), type(d1))
887 #with other type, right
888 c = 5 - d1
889 self.assertEqual(c, Decimal('16.1'))
890 self.assertEqual(type(c), type(d1))
892 #inline with decimal
893 d1 -= d2
894 self.assertEqual(d1, Decimal('-33.3'))
896 #inline with other type
897 d1 -= 5
898 self.assertEqual(d1, Decimal('-38.3'))
900 def test_multiplication(self):
902 d1 = Decimal('-5')
903 d2 = Decimal('3')
905 #two Decimals
906 self.assertEqual(d1*d2, Decimal('-15'))
907 self.assertEqual(d2*d1, Decimal('-15'))
909 #with other type, left
910 c = d1 * 5
911 self.assertEqual(c, Decimal('-25'))
912 self.assertEqual(type(c), type(d1))
914 #with other type, right
915 c = 5 * d1
916 self.assertEqual(c, Decimal('-25'))
917 self.assertEqual(type(c), type(d1))
919 #inline with decimal
920 d1 *= d2
921 self.assertEqual(d1, Decimal('-15'))
923 #inline with other type
924 d1 *= 5
925 self.assertEqual(d1, Decimal('-75'))
927 def test_division(self):
929 d1 = Decimal('-5')
930 d2 = Decimal('2')
932 #two Decimals
933 self.assertEqual(d1/d2, Decimal('-2.5'))
934 self.assertEqual(d2/d1, Decimal('-0.4'))
936 #with other type, left
937 c = d1 / 4
938 self.assertEqual(c, Decimal('-1.25'))
939 self.assertEqual(type(c), type(d1))
941 #with other type, right
942 c = 4 / d1
943 self.assertEqual(c, Decimal('-0.8'))
944 self.assertEqual(type(c), type(d1))
946 #inline with decimal
947 d1 /= d2
948 self.assertEqual(d1, Decimal('-2.5'))
950 #inline with other type
951 d1 /= 4
952 self.assertEqual(d1, Decimal('-0.625'))
954 def test_floor_division(self):
956 d1 = Decimal('5')
957 d2 = Decimal('2')
959 #two Decimals
960 self.assertEqual(d1//d2, Decimal('2'))
961 self.assertEqual(d2//d1, Decimal('0'))
963 #with other type, left
964 c = d1 // 4
965 self.assertEqual(c, Decimal('1'))
966 self.assertEqual(type(c), type(d1))
968 #with other type, right
969 c = 7 // d1
970 self.assertEqual(c, Decimal('1'))
971 self.assertEqual(type(c), type(d1))
973 #inline with decimal
974 d1 //= d2
975 self.assertEqual(d1, Decimal('2'))
977 #inline with other type
978 d1 //= 2
979 self.assertEqual(d1, Decimal('1'))
981 def test_powering(self):
983 d1 = Decimal('5')
984 d2 = Decimal('2')
986 #two Decimals
987 self.assertEqual(d1**d2, Decimal('25'))
988 self.assertEqual(d2**d1, Decimal('32'))
990 #with other type, left
991 c = d1 ** 4
992 self.assertEqual(c, Decimal('625'))
993 self.assertEqual(type(c), type(d1))
995 #with other type, right
996 c = 7 ** d1
997 self.assertEqual(c, Decimal('16807'))
998 self.assertEqual(type(c), type(d1))
1000 #inline with decimal
1001 d1 **= d2
1002 self.assertEqual(d1, Decimal('25'))
1004 #inline with other type
1005 d1 **= 4
1006 self.assertEqual(d1, Decimal('390625'))
1008 def test_module(self):
1010 d1 = Decimal('5')
1011 d2 = Decimal('2')
1013 #two Decimals
1014 self.assertEqual(d1%d2, Decimal('1'))
1015 self.assertEqual(d2%d1, Decimal('2'))
1017 #with other type, left
1018 c = d1 % 4
1019 self.assertEqual(c, Decimal('1'))
1020 self.assertEqual(type(c), type(d1))
1022 #with other type, right
1023 c = 7 % d1
1024 self.assertEqual(c, Decimal('2'))
1025 self.assertEqual(type(c), type(d1))
1027 #inline with decimal
1028 d1 %= d2
1029 self.assertEqual(d1, Decimal('1'))
1031 #inline with other type
1032 d1 %= 4
1033 self.assertEqual(d1, Decimal('1'))
1035 def test_floor_div_module(self):
1037 d1 = Decimal('5')
1038 d2 = Decimal('2')
1040 #two Decimals
1041 (p, q) = divmod(d1, d2)
1042 self.assertEqual(p, Decimal('2'))
1043 self.assertEqual(q, Decimal('1'))
1044 self.assertEqual(type(p), type(d1))
1045 self.assertEqual(type(q), type(d1))
1047 #with other type, left
1048 (p, q) = divmod(d1, 4)
1049 self.assertEqual(p, Decimal('1'))
1050 self.assertEqual(q, Decimal('1'))
1051 self.assertEqual(type(p), type(d1))
1052 self.assertEqual(type(q), type(d1))
1054 #with other type, right
1055 (p, q) = divmod(7, d1)
1056 self.assertEqual(p, Decimal('1'))
1057 self.assertEqual(q, Decimal('2'))
1058 self.assertEqual(type(p), type(d1))
1059 self.assertEqual(type(q), type(d1))
1061 def test_unary_operators(self):
1062 self.assertEqual(+Decimal(45), Decimal(+45)) # +
1063 self.assertEqual(-Decimal(45), Decimal(-45)) # -
1064 self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs
1066 def test_nan_comparisons(self):
1067 n = Decimal('NaN')
1068 s = Decimal('sNaN')
1069 i = Decimal('Inf')
1070 f = Decimal('2')
1071 for x, y in [(n, n), (n, i), (i, n), (n, f), (f, n),
1072 (s, n), (n, s), (s, i), (i, s), (s, f), (f, s), (s, s)]:
1073 self.assertTrue(x != y)
1074 self.assertTrue(not (x == y))
1075 self.assertTrue(not (x < y))
1076 self.assertTrue(not (x <= y))
1077 self.assertTrue(not (x > y))
1078 self.assertTrue(not (x >= y))
1080 # The following are two functions used to test threading in the next class
1082 def thfunc1(cls):
1083 d1 = Decimal(1)
1084 d3 = Decimal(3)
1085 test1 = d1/d3
1086 cls.synchro.wait()
1087 test2 = d1/d3
1088 cls.finish1.set()
1090 cls.assertEqual(test1, Decimal('0.3333333333333333333333333333'))
1091 cls.assertEqual(test2, Decimal('0.3333333333333333333333333333'))
1092 return
1094 def thfunc2(cls):
1095 d1 = Decimal(1)
1096 d3 = Decimal(3)
1097 test1 = d1/d3
1098 thiscontext = getcontext()
1099 thiscontext.prec = 18
1100 test2 = d1/d3
1101 cls.synchro.set()
1102 cls.finish2.set()
1104 cls.assertEqual(test1, Decimal('0.3333333333333333333333333333'))
1105 cls.assertEqual(test2, Decimal('0.333333333333333333'))
1106 return
1109 class DecimalUseOfContextTest(unittest.TestCase):
1110 '''Unit tests for Use of Context cases in Decimal.'''
1112 try:
1113 import threading
1114 except ImportError:
1115 threading = None
1117 # Take care executing this test from IDLE, there's an issue in threading
1118 # that hangs IDLE and I couldn't find it
1120 def test_threading(self):
1121 #Test the "threading isolation" of a Context.
1123 self.synchro = threading.Event()
1124 self.finish1 = threading.Event()
1125 self.finish2 = threading.Event()
1127 th1 = threading.Thread(target=thfunc1, args=(self,))
1128 th2 = threading.Thread(target=thfunc2, args=(self,))
1130 th1.start()
1131 th2.start()
1133 self.finish1.wait()
1134 self.finish2.wait()
1135 return
1137 if threading is None:
1138 del test_threading
1141 class DecimalUsabilityTest(unittest.TestCase):
1142 '''Unit tests for Usability cases of Decimal.'''
1144 def test_comparison_operators(self):
1146 da = Decimal('23.42')
1147 db = Decimal('23.42')
1148 dc = Decimal('45')
1150 #two Decimals
1151 self.assertTrue(dc > da)
1152 self.assertTrue(dc >= da)
1153 self.assertTrue(da < dc)
1154 self.assertTrue(da <= dc)
1155 self.assertTrue(da == db)
1156 self.assertTrue(da != dc)
1157 self.assertTrue(da <= db)
1158 self.assertTrue(da >= db)
1159 self.assertEqual(cmp(dc,da), 1)
1160 self.assertEqual(cmp(da,dc), -1)
1161 self.assertEqual(cmp(da,db), 0)
1163 #a Decimal and an int
1164 self.assertTrue(dc > 23)
1165 self.assertTrue(23 < dc)
1166 self.assertTrue(dc == 45)
1167 self.assertEqual(cmp(dc,23), 1)
1168 self.assertEqual(cmp(23,dc), -1)
1169 self.assertEqual(cmp(dc,45), 0)
1171 #a Decimal and uncomparable
1172 self.assertNotEqual(da, 'ugly')
1173 self.assertNotEqual(da, 32.7)
1174 self.assertNotEqual(da, object())
1175 self.assertNotEqual(da, object)
1177 # sortable
1178 a = map(Decimal, xrange(100))
1179 b = a[:]
1180 random.shuffle(a)
1181 a.sort()
1182 self.assertEqual(a, b)
1184 # with None
1185 self.assertFalse(Decimal(1) < None)
1186 self.assertTrue(Decimal(1) > None)
1188 def test_copy_and_deepcopy_methods(self):
1189 d = Decimal('43.24')
1190 c = copy.copy(d)
1191 self.assertEqual(id(c), id(d))
1192 dc = copy.deepcopy(d)
1193 self.assertEqual(id(dc), id(d))
1195 def test_hash_method(self):
1196 #just that it's hashable
1197 hash(Decimal(23))
1199 test_values = [Decimal(sign*(2**m + n))
1200 for m in [0, 14, 15, 16, 17, 30, 31,
1201 32, 33, 62, 63, 64, 65, 66]
1202 for n in range(-10, 10)
1203 for sign in [-1, 1]]
1204 test_values.extend([
1205 Decimal("-0"), # zeros
1206 Decimal("0.00"),
1207 Decimal("-0.000"),
1208 Decimal("0E10"),
1209 Decimal("-0E12"),
1210 Decimal("10.0"), # negative exponent
1211 Decimal("-23.00000"),
1212 Decimal("1230E100"), # positive exponent
1213 Decimal("-4.5678E50"),
1214 # a value for which hash(n) != hash(n % (2**64-1))
1215 # in Python pre-2.6
1216 Decimal(2**64 + 2**32 - 1),
1217 # selection of values which fail with the old (before
1218 # version 2.6) long.__hash__
1219 Decimal("1.634E100"),
1220 Decimal("90.697E100"),
1221 Decimal("188.83E100"),
1222 Decimal("1652.9E100"),
1223 Decimal("56531E100"),
1226 # check that hash(d) == hash(int(d)) for integral values
1227 for value in test_values:
1228 self.assertEqual(hash(value), hash(int(value)))
1230 #the same hash that to an int
1231 self.assertEqual(hash(Decimal(23)), hash(23))
1232 self.assertRaises(TypeError, hash, Decimal('NaN'))
1233 self.assertTrue(hash(Decimal('Inf')))
1234 self.assertTrue(hash(Decimal('-Inf')))
1236 # check that the value of the hash doesn't depend on the
1237 # current context (issue #1757)
1238 c = getcontext()
1239 old_precision = c.prec
1240 x = Decimal("123456789.1")
1242 c.prec = 6
1243 h1 = hash(x)
1244 c.prec = 10
1245 h2 = hash(x)
1246 c.prec = 16
1247 h3 = hash(x)
1249 self.assertEqual(h1, h2)
1250 self.assertEqual(h1, h3)
1251 c.prec = old_precision
1253 def test_min_and_max_methods(self):
1255 d1 = Decimal('15.32')
1256 d2 = Decimal('28.5')
1257 l1 = 15
1258 l2 = 28
1260 #between Decimals
1261 self.assertTrue(min(d1,d2) is d1)
1262 self.assertTrue(min(d2,d1) is d1)
1263 self.assertTrue(max(d1,d2) is d2)
1264 self.assertTrue(max(d2,d1) is d2)
1266 #between Decimal and long
1267 self.assertTrue(min(d1,l2) is d1)
1268 self.assertTrue(min(l2,d1) is d1)
1269 self.assertTrue(max(l1,d2) is d2)
1270 self.assertTrue(max(d2,l1) is d2)
1272 def test_as_nonzero(self):
1273 #as false
1274 self.assertFalse(Decimal(0))
1275 #as true
1276 self.assertTrue(Decimal('0.372'))
1278 def test_tostring_methods(self):
1279 #Test str and repr methods.
1281 d = Decimal('15.32')
1282 self.assertEqual(str(d), '15.32') # str
1283 self.assertEqual(repr(d), "Decimal('15.32')") # repr
1285 # result type of string methods should be str, not unicode
1286 unicode_inputs = [u'123.4', u'0.5E2', u'Infinity', u'sNaN',
1287 u'-0.0E100', u'-NaN001', u'-Inf']
1289 for u in unicode_inputs:
1290 d = Decimal(u)
1291 self.assertEqual(type(str(d)), str)
1292 self.assertEqual(type(repr(d)), str)
1293 self.assertEqual(type(d.to_eng_string()), str)
1295 def test_tonum_methods(self):
1296 #Test float, int and long methods.
1298 d1 = Decimal('66')
1299 d2 = Decimal('15.32')
1301 #int
1302 self.assertEqual(int(d1), 66)
1303 self.assertEqual(int(d2), 15)
1305 #long
1306 self.assertEqual(long(d1), 66)
1307 self.assertEqual(long(d2), 15)
1309 #float
1310 self.assertEqual(float(d1), 66)
1311 self.assertEqual(float(d2), 15.32)
1313 def test_eval_round_trip(self):
1315 #with zero
1316 d = Decimal( (0, (0,), 0) )
1317 self.assertEqual(d, eval(repr(d)))
1319 #int
1320 d = Decimal( (1, (4, 5), 0) )
1321 self.assertEqual(d, eval(repr(d)))
1323 #float
1324 d = Decimal( (0, (4, 5, 3, 4), -2) )
1325 self.assertEqual(d, eval(repr(d)))
1327 #weird
1328 d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
1329 self.assertEqual(d, eval(repr(d)))
1331 def test_as_tuple(self):
1333 #with zero
1334 d = Decimal(0)
1335 self.assertEqual(d.as_tuple(), (0, (0,), 0) )
1337 #int
1338 d = Decimal(-45)
1339 self.assertEqual(d.as_tuple(), (1, (4, 5), 0) )
1341 #complicated string
1342 d = Decimal("-4.34913534E-17")
1343 self.assertEqual(d.as_tuple(), (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
1345 #inf
1346 d = Decimal("Infinity")
1347 self.assertEqual(d.as_tuple(), (0, (0,), 'F') )
1349 #leading zeros in coefficient should be stripped
1350 d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), -2) )
1351 self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), -2) )
1352 d = Decimal( (1, (0, 0, 0), 37) )
1353 self.assertEqual(d.as_tuple(), (1, (0,), 37))
1354 d = Decimal( (1, (), 37) )
1355 self.assertEqual(d.as_tuple(), (1, (0,), 37))
1357 #leading zeros in NaN diagnostic info should be stripped
1358 d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), 'n') )
1359 self.assertEqual(d.as_tuple(), (0, (4, 0, 5, 3, 4), 'n') )
1360 d = Decimal( (1, (0, 0, 0), 'N') )
1361 self.assertEqual(d.as_tuple(), (1, (), 'N') )
1362 d = Decimal( (1, (), 'n') )
1363 self.assertEqual(d.as_tuple(), (1, (), 'n') )
1365 #coefficient in infinity should be ignored
1366 d = Decimal( (0, (4, 5, 3, 4), 'F') )
1367 self.assertEqual(d.as_tuple(), (0, (0,), 'F'))
1368 d = Decimal( (1, (0, 2, 7, 1), 'F') )
1369 self.assertEqual(d.as_tuple(), (1, (0,), 'F'))
1371 def test_immutability_operations(self):
1372 # Do operations and check that it didn't change change internal objects.
1374 d1 = Decimal('-25e55')
1375 b1 = Decimal('-25e55')
1376 d2 = Decimal('33e+33')
1377 b2 = Decimal('33e+33')
1379 def checkSameDec(operation, useOther=False):
1380 if useOther:
1381 eval("d1." + operation + "(d2)")
1382 self.assertEqual(d1._sign, b1._sign)
1383 self.assertEqual(d1._int, b1._int)
1384 self.assertEqual(d1._exp, b1._exp)
1385 self.assertEqual(d2._sign, b2._sign)
1386 self.assertEqual(d2._int, b2._int)
1387 self.assertEqual(d2._exp, b2._exp)
1388 else:
1389 eval("d1." + operation + "()")
1390 self.assertEqual(d1._sign, b1._sign)
1391 self.assertEqual(d1._int, b1._int)
1392 self.assertEqual(d1._exp, b1._exp)
1393 return
1395 Decimal(d1)
1396 self.assertEqual(d1._sign, b1._sign)
1397 self.assertEqual(d1._int, b1._int)
1398 self.assertEqual(d1._exp, b1._exp)
1400 checkSameDec("__abs__")
1401 checkSameDec("__add__", True)
1402 checkSameDec("__div__", True)
1403 checkSameDec("__divmod__", True)
1404 checkSameDec("__eq__", True)
1405 checkSameDec("__ne__", True)
1406 checkSameDec("__le__", True)
1407 checkSameDec("__lt__", True)
1408 checkSameDec("__ge__", True)
1409 checkSameDec("__gt__", True)
1410 checkSameDec("__float__")
1411 checkSameDec("__floordiv__", True)
1412 checkSameDec("__hash__")
1413 checkSameDec("__int__")
1414 checkSameDec("__trunc__")
1415 checkSameDec("__long__")
1416 checkSameDec("__mod__", True)
1417 checkSameDec("__mul__", True)
1418 checkSameDec("__neg__")
1419 checkSameDec("__nonzero__")
1420 checkSameDec("__pos__")
1421 checkSameDec("__pow__", True)
1422 checkSameDec("__radd__", True)
1423 checkSameDec("__rdiv__", True)
1424 checkSameDec("__rdivmod__", True)
1425 checkSameDec("__repr__")
1426 checkSameDec("__rfloordiv__", True)
1427 checkSameDec("__rmod__", True)
1428 checkSameDec("__rmul__", True)
1429 checkSameDec("__rpow__", True)
1430 checkSameDec("__rsub__", True)
1431 checkSameDec("__str__")
1432 checkSameDec("__sub__", True)
1433 checkSameDec("__truediv__", True)
1434 checkSameDec("adjusted")
1435 checkSameDec("as_tuple")
1436 checkSameDec("compare", True)
1437 checkSameDec("max", True)
1438 checkSameDec("min", True)
1439 checkSameDec("normalize")
1440 checkSameDec("quantize", True)
1441 checkSameDec("remainder_near", True)
1442 checkSameDec("same_quantum", True)
1443 checkSameDec("sqrt")
1444 checkSameDec("to_eng_string")
1445 checkSameDec("to_integral")
1447 def test_subclassing(self):
1448 # Different behaviours when subclassing Decimal
1450 class MyDecimal(Decimal):
1451 pass
1453 d1 = MyDecimal(1)
1454 d2 = MyDecimal(2)
1455 d = d1 + d2
1456 self.assertTrue(type(d) is Decimal)
1458 d = d1.max(d2)
1459 self.assertTrue(type(d) is Decimal)
1461 def test_implicit_context(self):
1462 # Check results when context given implicitly. (Issue 2478)
1463 c = getcontext()
1464 self.assertEqual(str(Decimal(0).sqrt()),
1465 str(c.sqrt(Decimal(0))))
1468 class DecimalPythonAPItests(unittest.TestCase):
1470 def test_abc(self):
1471 self.assertTrue(issubclass(Decimal, numbers.Number))
1472 self.assertTrue(not issubclass(Decimal, numbers.Real))
1473 self.assertTrue(isinstance(Decimal(0), numbers.Number))
1474 self.assertTrue(not isinstance(Decimal(0), numbers.Real))
1476 def test_pickle(self):
1477 d = Decimal('-3.141590000')
1478 p = pickle.dumps(d)
1479 e = pickle.loads(p)
1480 self.assertEqual(d, e)
1482 def test_int(self):
1483 for x in range(-250, 250):
1484 s = '%0.2f' % (x / 100.0)
1485 # should work the same as for floats
1486 self.assertEqual(int(Decimal(s)), int(float(s)))
1487 # should work the same as to_integral in the ROUND_DOWN mode
1488 d = Decimal(s)
1489 r = d.to_integral(ROUND_DOWN)
1490 self.assertEqual(Decimal(int(d)), r)
1492 def test_trunc(self):
1493 for x in range(-250, 250):
1494 s = '%0.2f' % (x / 100.0)
1495 # should work the same as for floats
1496 self.assertEqual(int(Decimal(s)), int(float(s)))
1497 # should work the same as to_integral in the ROUND_DOWN mode
1498 d = Decimal(s)
1499 r = d.to_integral(ROUND_DOWN)
1500 self.assertEqual(Decimal(math.trunc(d)), r)
1502 def test_from_float(self):
1504 class MyDecimal(Decimal):
1505 pass
1507 r = MyDecimal.from_float(0.1)
1508 self.assertEqual(type(r), MyDecimal)
1509 self.assertEqual(str(r),
1510 '0.1000000000000000055511151231257827021181583404541015625')
1511 bigint = 12345678901234567890123456789
1512 self.assertEqual(MyDecimal.from_float(bigint), MyDecimal(bigint))
1513 self.assertTrue(MyDecimal.from_float(float('nan')).is_qnan())
1514 self.assertTrue(MyDecimal.from_float(float('inf')).is_infinite())
1515 self.assertTrue(MyDecimal.from_float(float('-inf')).is_infinite())
1516 self.assertEqual(str(MyDecimal.from_float(float('nan'))),
1517 str(Decimal('NaN')))
1518 self.assertEqual(str(MyDecimal.from_float(float('inf'))),
1519 str(Decimal('Infinity')))
1520 self.assertEqual(str(MyDecimal.from_float(float('-inf'))),
1521 str(Decimal('-Infinity')))
1522 self.assertRaises(TypeError, MyDecimal.from_float, 'abc')
1523 for i in range(200):
1524 x = random.expovariate(0.01) * (random.random() * 2.0 - 1.0)
1525 self.assertEqual(x, float(MyDecimal.from_float(x))) # roundtrip
1527 def test_create_decimal_from_float(self):
1528 context = Context(prec=5, rounding=ROUND_DOWN)
1529 self.assertEqual(
1530 context.create_decimal_from_float(math.pi),
1531 Decimal('3.1415')
1533 context = Context(prec=5, rounding=ROUND_UP)
1534 self.assertEqual(
1535 context.create_decimal_from_float(math.pi),
1536 Decimal('3.1416')
1538 context = Context(prec=5, traps=[Inexact])
1539 self.assertRaises(
1540 Inexact,
1541 context.create_decimal_from_float,
1542 math.pi
1544 self.assertEqual(repr(context.create_decimal_from_float(-0.0)),
1545 "Decimal('-0')")
1546 self.assertEqual(repr(context.create_decimal_from_float(1.0)),
1547 "Decimal('1')")
1548 self.assertEqual(repr(context.create_decimal_from_float(10)),
1549 "Decimal('10')")
1551 class ContextAPItests(unittest.TestCase):
1553 def test_pickle(self):
1554 c = Context()
1555 e = pickle.loads(pickle.dumps(c))
1556 for k in vars(c):
1557 v1 = vars(c)[k]
1558 v2 = vars(e)[k]
1559 self.assertEqual(v1, v2)
1561 def test_equality_with_other_types(self):
1562 self.assertTrue(Decimal(10) in ['a', 1.0, Decimal(10), (1,2), {}])
1563 self.assertTrue(Decimal(10) not in ['a', 1.0, (1,2), {}])
1565 def test_copy(self):
1566 # All copies should be deep
1567 c = Context()
1568 d = c.copy()
1569 self.assertNotEqual(id(c), id(d))
1570 self.assertNotEqual(id(c.flags), id(d.flags))
1571 self.assertNotEqual(id(c.traps), id(d.traps))
1573 class WithStatementTest(unittest.TestCase):
1574 # Can't do these as docstrings until Python 2.6
1575 # as doctest can't handle __future__ statements
1577 def test_localcontext(self):
1578 # Use a copy of the current context in the block
1579 orig_ctx = getcontext()
1580 with localcontext() as enter_ctx:
1581 set_ctx = getcontext()
1582 final_ctx = getcontext()
1583 self.assertTrue(orig_ctx is final_ctx, 'did not restore context correctly')
1584 self.assertTrue(orig_ctx is not set_ctx, 'did not copy the context')
1585 self.assertTrue(set_ctx is enter_ctx, '__enter__ returned wrong context')
1587 def test_localcontextarg(self):
1588 # Use a copy of the supplied context in the block
1589 orig_ctx = getcontext()
1590 new_ctx = Context(prec=42)
1591 with localcontext(new_ctx) as enter_ctx:
1592 set_ctx = getcontext()
1593 final_ctx = getcontext()
1594 self.assertTrue(orig_ctx is final_ctx, 'did not restore context correctly')
1595 self.assertTrue(set_ctx.prec == new_ctx.prec, 'did not set correct context')
1596 self.assertTrue(new_ctx is not set_ctx, 'did not copy the context')
1597 self.assertTrue(set_ctx is enter_ctx, '__enter__ returned wrong context')
1599 class ContextFlags(unittest.TestCase):
1600 def test_flags_irrelevant(self):
1601 # check that the result (numeric result + flags raised) of an
1602 # arithmetic operation doesn't depend on the current flags
1604 context = Context(prec=9, Emin = -999999999, Emax = 999999999,
1605 rounding=ROUND_HALF_EVEN, traps=[], flags=[])
1607 # operations that raise various flags, in the form (function, arglist)
1608 operations = [
1609 (context._apply, [Decimal("100E-1000000009")]),
1610 (context.sqrt, [Decimal(2)]),
1611 (context.add, [Decimal("1.23456789"), Decimal("9.87654321")]),
1612 (context.multiply, [Decimal("1.23456789"), Decimal("9.87654321")]),
1613 (context.subtract, [Decimal("1.23456789"), Decimal("9.87654321")]),
1616 # try various flags individually, then a whole lot at once
1617 flagsets = [[Inexact], [Rounded], [Underflow], [Clamped], [Subnormal],
1618 [Inexact, Rounded, Underflow, Clamped, Subnormal]]
1620 for fn, args in operations:
1621 # find answer and flags raised using a clean context
1622 context.clear_flags()
1623 ans = fn(*args)
1624 flags = [k for k, v in context.flags.items() if v]
1626 for extra_flags in flagsets:
1627 # set flags, before calling operation
1628 context.clear_flags()
1629 for flag in extra_flags:
1630 context._raise_error(flag)
1631 new_ans = fn(*args)
1633 # flags that we expect to be set after the operation
1634 expected_flags = list(flags)
1635 for flag in extra_flags:
1636 if flag not in expected_flags:
1637 expected_flags.append(flag)
1638 expected_flags.sort()
1640 # flags we actually got
1641 new_flags = [k for k,v in context.flags.items() if v]
1642 new_flags.sort()
1644 self.assertEqual(ans, new_ans,
1645 "operation produces different answers depending on flags set: " +
1646 "expected %s, got %s." % (ans, new_ans))
1647 self.assertEqual(new_flags, expected_flags,
1648 "operation raises different flags depending on flags set: " +
1649 "expected %s, got %s" % (expected_flags, new_flags))
1651 def test_main(arith=False, verbose=None, todo_tests=None, debug=None):
1652 """ Execute the tests.
1654 Runs all arithmetic tests if arith is True or if the "decimal" resource
1655 is enabled in regrtest.py
1658 init()
1659 global TEST_ALL, DEBUG
1660 TEST_ALL = arith or is_resource_enabled('decimal')
1661 DEBUG = debug
1663 if todo_tests is None:
1664 test_classes = [
1665 DecimalExplicitConstructionTest,
1666 DecimalImplicitConstructionTest,
1667 DecimalArithmeticOperatorsTest,
1668 DecimalFormatTest,
1669 DecimalUseOfContextTest,
1670 DecimalUsabilityTest,
1671 DecimalPythonAPItests,
1672 ContextAPItests,
1673 DecimalTest,
1674 WithStatementTest,
1675 ContextFlags
1677 else:
1678 test_classes = [DecimalTest]
1680 # Dynamically build custom test definition for each file in the test
1681 # directory and add the definitions to the DecimalTest class. This
1682 # procedure insures that new files do not get skipped.
1683 for filename in os.listdir(directory):
1684 if '.decTest' not in filename or filename.startswith("."):
1685 continue
1686 head, tail = filename.split('.')
1687 if todo_tests is not None and head not in todo_tests:
1688 continue
1689 tester = lambda self, f=filename: self.eval_file(directory + f)
1690 setattr(DecimalTest, 'test_' + head, tester)
1691 del filename, head, tail, tester
1694 try:
1695 run_unittest(*test_classes)
1696 if todo_tests is None:
1697 import decimal as DecimalModule
1698 run_doctest(DecimalModule, verbose)
1699 finally:
1700 setcontext(ORIGINAL_CONTEXT)
1702 if __name__ == '__main__':
1703 import optparse
1704 p = optparse.OptionParser("test_decimal.py [--debug] [{--skip | test1 [test2 [...]]}]")
1705 p.add_option('--debug', '-d', action='store_true', help='shows the test number and context before each test')
1706 p.add_option('--skip', '-s', action='store_true', help='skip over 90% of the arithmetic tests')
1707 (opt, args) = p.parse_args()
1709 if opt.skip:
1710 test_main(arith=False, verbose=True)
1711 elif args:
1712 test_main(arith=True, verbose=True, todo_tests=args, debug=opt.debug)
1713 else:
1714 test_main(arith=True, verbose=True)