New in version 2.4.
The decimal module
provides support for decimal floating point arithmetic. It offers
several advantages over the
float datatype:

Decimal numbers can be represented exactly. In contrast, numbers
like 1.1
do not have an exact representation in binary
floating point. End users typically would not expect
1.1
to display as 1.1000000000000001
as it does
with binary floating point.

The exactness carries over into arithmetic. In decimal floating
point, “0.1 + 0.1 + 0.1  0.3
” is exactly equal to
zero. In binary floating point, result is
5.5511151231257827e017
. While near to zero, the differences
prevent reliable equality testing and differences can accumulate.
For this reason, decimal would be preferred in accounting
applications which have strict equality invariants.

The decimal module incorporates a notion of significant places
so that “1.30 + 1.20
” is 2.50
. The
trailing zero is kept to indicate significance. This is the
customary presentation for monetary applications. For
multiplication, the “schoolbook” approach uses all the
figures in the multiplicands. For instance, “1.3 *
1.2
” gives 1.56
while “1.30 *
1.20
” gives 1.5600
.

Unlike hardware based binary floating point, the decimal module
has a user settable precision (defaulting to 28 places) which can
be as large as needed for a given problem:
>>> getcontext().prec = 6
>>> Decimal(1) / Decimal(7)
Decimal("0.142857")
>>> getcontext().prec = 28
>>> Decimal(1) / Decimal(7)
Decimal("0.1428571428571428571428571429")

Both binary and decimal floating point are implemented in terms
of published standards. While the builtin float type exposes only
a modest portion of its capabilities, the decimal module exposes
all required parts of the standard. When needed, the programmer has
full control over rounding and signal handling.
The module design is centered around three concepts: the decimal
number, the context for arithmetic, and signals.
A decimal number is immutable. It has a sign, coefficient
digits, and an exponent. To preserve significance, the coefficient
digits do not truncate trailing zeroes. Decimals also include
special values such as Infinity
,
Infinity
, and NaN
. The standard also
differentiates 0
from +0
.
The context for arithmetic is an environment specifying
precision, rounding rules, limits on exponents, flags indicating
the results of operations, and trap enablers which determine
whether signals are treated as exceptions. Rounding options include
ROUND_CEILING
, ROUND_DOWN
,
ROUND_FLOOR
, ROUND_HALF_DOWN
,
ROUND_HALF_EVEN
, ROUND_HALF_UP
, and
ROUND_UP
.
Signals are groups of exceptional conditions arising during the
course of computation. Depending on the needs of the application,
signals may be ignored, considered as informational, or treated as
exceptions. The signals in the decimal module are:
Clamped
, InvalidOperation
,
DivisionByZero
, Inexact
, Rounded
,
Subnormal
, Overflow
, and
Underflow
.
For each signal there is a flag and a trap enabler. When a
signal is encountered, its flag incremented from zero and, then, if
the trap enabler is set to one, an exception is raised. Flags are
sticky, so the user needs to reset them before monitoring a
calculation.
Quickstart Tutorial
The usual start to using decimals is importing the module,
viewing the current context with getcontext and, if necessary, setting
new values for precision, rounding, or enabled traps:
>>> from decimal import *
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=999999999, Emax=999999999,
capitals=1, flags=[], traps=[Overflow, InvalidOperation,
DivisionByZero])
>>> getcontext().prec = 7 # Set a new precision
Decimal instances can be constructed from integers, strings or
tuples. To create a Decimal from a float, first convert it to a string. This
serves as an explicit reminder of the details of the conversion
(including representation error). Decimal numbers include special
values such as NaN
which stands for “Not a
number”, positive and negative Infinity
, and
0
.
>>> Decimal(10)
Decimal("10")
>>> Decimal("3.14")
Decimal("3.14")
>>> Decimal((0, (3, 1, 4), 2))
Decimal("3.14")
>>> Decimal(str(2.0 ** 0.5))
Decimal("1.41421356237")
>>> Decimal("NaN")
Decimal("NaN")
>>> Decimal("Infinity")
Decimal("Infinity")
The significance of a new Decimal is determined solely by the
number of digits input. Context precision and rounding only come
into play during arithmetic operations.
>>> getcontext().prec = 6
>>> Decimal('3.0')
Decimal("3.0")
>>> Decimal('3.1415926535')
Decimal("3.1415926535")
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal("5.85987")
>>> getcontext().rounding = ROUND_UP
>>> Decimal('3.1415926535') + Decimal('2.7182818285')
Decimal("5.85988")
Decimals interact well with much of the rest of python. Here is
a small decimal floating point flying circus:
>>> data = map(Decimal, '1.34 1.87 3.45 2.35 1.00 0.03 9.25'.split())
>>> max(data)
Decimal("9.25")
>>> min(data)
Decimal("0.03")
>>> sorted(data)
[Decimal("0.03"), Decimal("1.00"), Decimal("1.34"), Decimal("1.87"),
Decimal("2.35"), Decimal("3.45"), Decimal("9.25")]
>>> sum(data)
Decimal("19.29")
>>> a,b,c = data[:3]
>>> str(a)
'1.34'
>>> float(a)
1.3400000000000001
>>> round(a, 1) # round() first converts to binary floating point
1.3
>>> int(a)
1
>>> a * 5
Decimal("6.70")
>>> a * b
Decimal("2.5058")
>>> c % a
Decimal("0.77")
The quantize method
rounds a number to a fixed exponent. This method is useful for
monetary applications that often round results to a fixed number of
places:
>>> Decimal('7.325').quantize(Decimal('.01'), rounding=ROUND_DOWN)
Decimal("7.32")
>>> Decimal('7.325').quantize(Decimal('1.'), rounding=ROUND_UP)
Decimal("8")
As shown above, the
getcontext function accesses the current context and allows the
settings to be changed. This approach meets the needs of most
applications.
For more advanced work, it may be useful to create alternate
contexts using the Context() constructor. To make an alternate
active, use the
setcontext function.
In accordance with the standard, the Decimal module provides two ready to use
standard contexts, BasicContext
and
ExtendedContext
. The former is especially useful for
debugging because many of the traps are enabled:
>>> myothercontext = Context(prec=60, rounding=ROUND_HALF_DOWN)
>>> setcontext(myothercontext)
>>> Decimal(1) / Decimal(7)
Decimal("0.142857142857142857142857142857142857142857142857142857142857")
>>> ExtendedContext
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=999999999, Emax=999999999,
capitals=1, flags=[], traps=[])
>>> setcontext(ExtendedContext)
>>> Decimal(1) / Decimal(7)
Decimal("0.142857143")
>>> Decimal(42) / Decimal(0)
Decimal("Infinity")
>>> setcontext(BasicContext)
>>> Decimal(42) / Decimal(0)
Traceback (most recent call last):
File "<pyshell#143>", line 1, in toplevel
Decimal(42) / Decimal(0)
DivisionByZero: x / 0
Contexts also have signal flags for monitoring exceptional
conditions encountered during computations. The flags remain set
until explicitly cleared, so it is best to clear the flags before
each set of monitored computations by using the clear_flags method.
>>> setcontext(ExtendedContext)
>>> getcontext().clear_flags()
>>> Decimal(355) / Decimal(113)
Decimal("3.14159292")
>>> getcontext()
Context(prec=9, rounding=ROUND_HALF_EVEN, Emin=999999999, Emax=999999999,
capitals=1, flags=[Inexact, Rounded], traps=[])
The flags entry shows that the rational approximation
to Pi
was rounded (digits beyond the context precision
were thrown away) and that the result is inexact (some of the
discarded digits were nonzero).
Individual traps are set using the dictionary in the traps field of a context:
>>> Decimal(1) / Decimal(0)
Decimal("Infinity")
>>> getcontext().traps[DivisionByZero] = 1
>>> Decimal(1) / Decimal(0)
Traceback (most recent call last):
File "<pyshell#112>", line 1, in toplevel
Decimal(1) / Decimal(0)
DivisionByZero: x / 0
Most programs adjust the current context only once, at the
beginning of the program. And, in many applications, data is
converted to Decimal with a
single cast inside a loop. With context set and decimals created,
the bulk of the program manipulates the data no differently than
with other Python numeric types.
Decimal objects
Mitigating roundoff error with increased precision
The use of decimal floating point eliminates decimal
representation error (making it possible to represent
0.1
exactly); however, some operations can still incur
roundoff error when nonzero digits exceed the fixed
precision.
The effects of roundoff error can be amplified by the addition
or subtraction of nearly offsetting quantities resulting in loss of
significance. Knuth provides two instructive examples where rounded
floating point arithmetic with insufficient precision causes the
breakdown of the associative and distributive properties of
addition:
# Examples from Seminumerical Algorithms, Section 4.2.2.
>>> from decimal import *
>>> getcontext().prec = 8
>>> u, v, w = Decimal(11111113), Decimal(11111111), Decimal('7.51111111')
>>> (u + v) + w
Decimal("9.5111111")
>>> u + (v + w)
Decimal("10")
>>> u, v, w = Decimal(20000), Decimal(6), Decimal('6.0000003')
>>> (u*v) + (u*w)
Decimal("0.01")
>>> u * (v+w)
Decimal("0.0060000")
The decimal module makes
it possible to restore the identities by expanding the precision
sufficiently to avoid loss of significance:
>>> getcontext().prec = 20
>>> u, v, w = Decimal(11111113), Decimal(11111111), Decimal('7.51111111')
>>> (u + v) + w
Decimal("9.51111111")
>>> u + (v + w)
Decimal("9.51111111")
>>>
>>> u, v, w = Decimal(20000), Decimal(6), Decimal('6.0000003')
>>> (u*v) + (u*w)
Decimal("0.0060000")
>>> u * (v+w)
Decimal("0.0060000")
Special values
The number system for the
decimal module provides special values including
NaN
, sNaN
, Infinity
,
Infinity
, and two zeroes, +0
and
0
.
Infinities can be constructed directly with:
Decimal('Infinity')
. Also, they can arise from dividing by
zero when the
DivisionByZero signal is not trapped. Likewise, when the Overflow signal is not
trapped, infinity can result from rounding beyond the limits of the
largest representable number.
The infinities are signed (affine) and can be used in arithmetic
operations where they get treated as very large, indeterminate
numbers. For instance, adding a constant to infinity gives another
infinite result.
Some operations are indeterminate and return NaN
,
or if the
InvalidOperation signal is trapped, raise an exception. For
example, 0/0
returns NaN
which means
“not a number”. This variety of NaN
is
quiet and, once created, will flow through other computations
always resulting in another NaN
. This behavior can be
useful for a series of computations that occasionally have missing
inputs — it allows the calculation to proceed while flagging
specific results as invalid.
A variant is sNaN
which signals rather than
remaining quiet after every operation. This is a useful return
value when an invalid result needs to interrupt a calculation for
special handling.
The signed zeros can result from calculations that underflow.
They keep the sign that would have resulted if the calculation had
been carried out to greater precision. Since their magnitude is
zero, both positive and negative zeros are treated as equal and
their sign is informational.
In addition to the two signed zeros which are distinct yet
equal, there are various representations of zero with differing
precisions yet equivalent in value. This takes a bit of getting
used to. For an eye accustomed to normalized floating point
representations, it is not immediately obvious that the following
calculation returns a value equal to zero:
>>> 1 / Decimal('Infinity')
Decimal("0E1000000026")
Working with threads
The getcontext
function accesses a different
Context object for each thread. Having separate thread contexts
means that threads may make changes (such as
getcontext.prec=10
) without interfering with other
threads.
Likewise, the
setcontext function automatically assigns its target to the
current thread.
If setcontext has not
been called before
getcontext, then
getcontext will automatically create a new context for use in
the current thread.
The new context is copied from a prototype context called
DefaultContext. To control the defaults so that each thread
will use the same values throughout the application, directly
modify the DefaultContext object. This should be done
before any threads are started so that there won’t be a
race condition between threads calling getcontext. For example:
# Set applicationwide defaults for all threads about to be launched
DefaultContext.prec = 12
DefaultContext.rounding = ROUND_DOWN
DefaultContext.traps = ExtendedContext.traps.copy()
DefaultContext.traps[InvalidOperation] = 1
setcontext(DefaultContext)
# Afterwards, the threads can be started
t1.start()
t2.start()
t3.start()
. . .
Recipes
Here are a few recipes that serve as utility functions and that
demonstrate ways to work with the Decimal class:
def moneyfmt(value, places=2, curr='', sep=',', dp='.',
pos='', neg='', trailneg=''):
"""Convert Decimal to a money formatted string.
places: required number of places after the decimal point
curr: optional currency symbol before the sign (may be blank)
sep: optional grouping separator (comma, period, space, or blank)
dp: decimal point indicator (comma or period)
only specify as blank when places is zero
pos: optional sign for positive numbers: '+', space or blank
neg: optional sign for negative numbers: '', '(', space or blank
trailneg:optional trailing minus indicator: '', ')', space or blank
>>> d = Decimal('1234567.8901')
>>> moneyfmt(d, curr='$')
'$1,234,567.89'
>>> moneyfmt(d, places=0, sep='.', dp='', neg='', trailneg='')
'1.234.568'
>>> moneyfmt(d, curr='$', neg='(', trailneg=')')
'($1,234,567.89)'
>>> moneyfmt(Decimal(123456789), sep=' ')
'123 456 789.00'
>>> moneyfmt(Decimal('0.02'), neg='<', trailneg='>')
'<.02>'
"""
q = Decimal((0, (1,), places)) # 2 places > '0.01'
sign, digits, exp = value.quantize(q).as_tuple()
assert exp == places
result = []
digits = map(str, digits)
build, next = result.append, digits.pop
if sign:
build(trailneg)
for i in range(places):
if digits:
build(next())
else:
build('0')
build(dp)
i = 0
while digits:
build(next())
i += 1
if i == 3 and digits:
i = 0
build(sep)
build(curr)
if sign:
build(neg)
else:
build(pos)
result.reverse()
return ''.join(result)
def pi():
"""Compute Pi to the current precision.
>>> print pi()
3.141592653589793238462643383
"""
getcontext().prec += 2 # extra digits for intermediate steps
three = Decimal(3) # substitute "three=3.0" for regular floats
lasts, t, s, n, na, d, da = 0, three, 3, 1, 0, 0, 24
while s != lasts:
lasts = s
n, na = n+na, na+8
d, da = d+da, da+32
t = (t * n) / d
s += t
getcontext().prec = 2
return +s # unary plus applies the new precision
def exp(x):
"""Return e raised to the power of x. Result type matches input type.
>>> print exp(Decimal(1))
2.718281828459045235360287471
>>> print exp(Decimal(2))
7.389056098930650227230427461
>>> print exp(2.0)
7.38905609893
>>> print exp(2+0j)
(7.38905609893+0j)
"""
getcontext().prec += 2
i, lasts, s, fact, num = 0, 0, 1, 1, 1
while s != lasts:
lasts = s
i += 1
fact *= i
num *= x
s += num / fact
getcontext().prec = 2
return +s
def cos(x):
"""Return the cosine of x as measured in radians.
>>> print cos(Decimal('0.5'))
0.8775825618903727161162815826
>>> print cos(0.5)
0.87758256189
>>> print cos(0.5+0j)
(0.87758256189+0j)
"""
getcontext().prec += 2
i, lasts, s, fact, num, sign = 0, 0, 1, 1, 1, 1
while s != lasts:
lasts = s
i += 2
fact *= i * (i1)
num *= x * x
sign *= 1
s += num / fact * sign
getcontext().prec = 2
return +s
def sin(x):
"""Return the sine of x as measured in radians.
>>> print sin(Decimal('0.5'))
0.4794255386042030002732879352
>>> print sin(0.5)
0.479425538604
>>> print sin(0.5+0j)
(0.479425538604+0j)
"""
getcontext().prec += 2
i, lasts, s, fact, num, sign = 1, 0, x, 1, x, 1
while s != lasts:
lasts = s
i += 2
fact *= i * (i1)
num *= x * x
sign *= 1
s += num / fact * sign
getcontext().prec = 2
return +s
Decimal FAQ
Q. It is cumbersome to type
decimal.Decimal('1234.5')
. Is there a way to minimize typing
when using the interactive interpreter?
A. Some users abbreviate the constructor to just a single
letter:
>>> D = decimal.Decimal
>>> D('1.23') + D('3.45')
Decimal("4.68")
Q. In a fixedpoint application with two decimal places, some
inputs have many places and need to be rounded. Others are not
supposed to have excess digits and need to be validated. What
methods should be used?
A. The quantize method
rounds to a fixed number of decimal places. If the
Inexact
trap is set, it is also useful for validation:
>>> TWOPLACES = Decimal(10) ** 2 # same as Decimal('0.01')
>>> # Round to two places
>>> Decimal("3.214").quantize(TWOPLACES)
Decimal("3.21")
>>> # Validate that a number does not exceed two places
>>> Decimal("3.21").quantize(TWOPLACES, context=Context(traps=[Inexact]))
Decimal("3.21")
>>> Decimal("3.214").quantize(TWOPLACES, context=Context(traps=[Inexact]))
Traceback (most recent call last):
...
Inexact: Changed in rounding
Q. Once I have valid two place inputs, how do I maintain that
invariant throughout an application?
A. Some operations like addition and subtraction automatically
preserve fixed point. Others, like multiplication and division,
change the number of decimal places and need to be followedup with
a quantize step.
Q. There are many ways to express the same value. The numbers
200
, 200.000
, 2E2
, and
.02E+4
all have the same value at various precisions.
Is there a way to transform them to a single recognizable canonical
value?
A. The normalize
method maps all equivalent values to a single representive:
>>> values = map(Decimal, '200 200.000 2E2 .02E+4'.split())
>>> [v.normalize() for v in values]
[Decimal("2E+2"), Decimal("2E+2"), Decimal("2E+2"), Decimal("2E+2")]
Q. Some decimal values always print with exponential notation.
Is there a way to get a nonexponential representation?
A. For some values, exponential notation is the only way to
express the number of significant places in the coefficient. For
example, expressing 5.0E+3
as 5000
keeps
the value constant but cannot show the original’s twoplace
significance.
Q. Is there a way to convert a regular float to a Decimal?
A. Yes, all binary floating point numbers can be exactly
expressed as a Decimal. An exact conversion may take more precision
than intuition would suggest, so trapping Inexact
will
signal a need for more precision:
def floatToDecimal(f):
"Convert a floating point number to a Decimal with no loss of information"
# Transform (exactly) a float to a mantissa (0.5 <= abs(m) < 1.0) and an
# exponent. Double the mantissa until it is an integer. Use the integer
# mantissa and exponent to compute an equivalent Decimal. If this cannot
# be done exactly, then retry with more precision.
mantissa, exponent = math.frexp(f)
while mantissa != int(mantissa):
mantissa *= 2.0
exponent = 1
mantissa = int(mantissa)
oldcontext = getcontext()
setcontext(Context(traps=[Inexact]))
try:
while True:
try:
return mantissa * Decimal(2) ** exponent
except Inexact:
getcontext().prec += 1
finally:
setcontext(oldcontext)
Q. Why isn’t the
floatToDecimal routine included in the module?
A. There is some question about whether it is advisable to mix
binary and decimal floating point. Also, its use requires some care
to avoid the representation issues associated with binary floating
point:
>>> floatToDecimal(1.1)
Decimal("1.100000000000000088817841970012523233890533447265625")
Q. Within a complex calculation, how can I make sure that I
haven’t gotten a spurious result because of insufficient precision
or rounding anomalies.
A. The decimal module makes it easy to test results. A best
practice is to rerun calculations using greater precision and with
various rounding modes. Widely differing results indicate
insufficient precision, rounding mode issues, illconditioned
inputs, or a numerically unstable algorithm.
Q. I noticed that context precision is applied to the results of
operations but not to the inputs. Is there anything to watch out
for when mixing values of different precisions?
A. Yes. The principle is that all values are considered to be
exact and so is the arithmetic on those values. Only the results
are rounded. The advantage for inputs is that “what you type
is what you get”. A disadvantage is that the results can look
odd if you forget that the inputs haven’t been rounded:
>>> getcontext().prec = 3
>>> Decimal('3.104') + D('2.104')
Decimal("5.21")
>>> Decimal('3.104') + D('0.000') + D('2.104')
Decimal("5.20")
The solution is either to increase precision or to force
rounding of inputs using the unary plus operation:
>>> getcontext().prec = 3
>>> +Decimal('1.23456789') # unary plus triggers rounding
Decimal("1.23")
Alternatively, inputs can be rounded upon creation using the
Context.create_decimal method:
>>> Context(prec=5, rounding=ROUND_DOWN).create_decimal('1.2345678')
Decimal("1.2345")