Python Decimal
Summary: in this tutorial, you’ll learn about the Python decimal
module that supports fast correctly-rounded decimal floating-point arithmetic.
Introduction to the Python decimal module
Many decimal numbers don’t have exact representations in binary floating-point such as 0.1. When using these numbers in arithmetic operations, you’ll get a result that you would not expect. For example:
x = 0.1
y = 0.1
z = 0.1s = x + y + z
print(s)
Code language: PHP (php)
Output:
0.30000000000000004
Code language: CSS (css)
The result is 0.30000000000000004, not 0.3.
To solve this problem, you use the Decimal
class from the decimal
module as follows:
import decimal
from decimal import Decimalx = Decimal('0.1')
y = Decimal('0.1')
z = Decimal('0.1')
s = x + y + z
print(s)
Code language: JavaScript (javascript)
Output:
0.3
Code language: CSS (css)
The output is as expected.
The Python decimal
module supports arithmetic that works the same as the arithmetic you learn at school.
Unlike floats, Python represents decimal numbers exactly. And the exactness carries over into arithmetic. For example, the following expression returns exactly 0.0:
Decimal('0.1') + Decimal('0.1') + Decimal('0.1') - Decimal('0.3')
Code language: JavaScript (javascript)
Decimal context
Decimal always associates with a context that controls the following aspects:
- Precision during an arithmetic operation
- Rounding algorithm
By default, the context is global. The global context is the default context. Also, you can set a temporary context that will take effect locally without affecting the global context.
To get the default context, you call the getcontext()
function from the decimal
module:
decimal.getcontext()
Code language: CSS (css)
The getcontext()
function returns the default context, which can be global or local.
To create a new context copied from another context, you use the localcontext()
function:
decimal.localcontext(ctx=None)
The localcontext()
returns a new context copied from the context ctx
if specified.
Once getting the context object, you can access the precision and rouding via the prec
and rounding
property respectively:
ctx.pre
: get or set the precision. The ctx.pre is an integer which defaults to 28ctx.rounding
: get or set the rounding mechanism. The rounding is a string. It defaults to'ROUND_HALF_EVEN'
. Note floats also use this rounding mechanism.
Python provides the following rounding mechanisms:
Rounding | Description |
---|---|
ROUND_UP | round away from zero |
ROUND_DOWN | round towards zero |
ROUND_CEILING | round to ceiling (towards positive infinity) |
ROUND_FLOOR | round to floor (towards negative infinity) |
ROUND_HALF_UP | round to nearest, ties away from zero |
ROUND_HALF_DOWN | round to nearest, ties towards zero |
ROUND_HALF_EVEN | round to nearest, ties to even (least significant digit) |
This example illustrates how to get the default precision and rounding of the default context:
import decimalctx = decimal.getcontext()
print(ctx.prec)
print(ctx.rounding)
Code language: PHP (php)
Output:
28
ROUND_HALF_EVEN
The following example shows how the 'ROUND_HALF_EVEN'
rounding mechanism takes effect:
import decimal
from decimal import Decimalx = Decimal('2.25')
y = Decimal('3.35')
print(round(x, 1))
print(round(y, 1))
Code language: JavaScript (javascript)
Output:
2.2
3.4
Code language: CSS (css)
If you change the rounding to 'ROUND_HALF_UP'
, you’ll get a different result:
import decimal
from decimal import Decimalctx = decimal.getcontext()
ctx.rounding = decimal.ROUND_HALF_UP
x = Decimal('2.25')
y = Decimal('3.35')
print(round(x, 1))
print(round(y, 1))
Code language: JavaScript (javascript)
Output:
2.3
3.4
Code language: CSS (css)
The following example shows you how to copy the default context and change the rounding to 'ROUND_HALF_UP'
:
import decimal
from decimal import Decimalx = Decimal('2.25')
y = Decimal('3.35')
with decimal.localcontext() as ctx:
print('Local context:')
ctx.rounding = decimal.ROUND_HALF_UP
print(round(x, 1))
print(round(y, 1))
print('Global context:')
print(round(x, 1))
print(round(y, 1))
Code language: PHP (php)
Output:
Local context:
2.3
3.4
Global context:
2.2
3.4
Code language: CSS (css)
Notice that the local context doesn’t affect the global context. After the with block, Python uses the default rounding mechanism.
Decimal constructor
The Decimal
constructor allows you to create a new Decimal
object based on a value:
Decimal(value='0', context=None)
Code language: JavaScript (javascript)
The value
argument can be an integer, string, tuple, float, or another Decimal object. If you don’t provide the value argument, it defaults to '0'
.
If the value is a tuple, it should have three components: a sign (0 for positive or 1 for negative), a tuple of digits, and an integer exponent:
(sign, (digit1,digit2, digit3,...), exponent)
For example:
3.14 = 314 x 10^-2
The tuple has three elements as follows:
- sign is 0
- digits is (3,1,4)
- exponent is -2
Therefore, you’ll need to pass the following tuple to the Decimal
constructor:
import decimal
from decimal import Decimalx = Decimal((0, (3, 1, 4), -2))
print(x)
Code language: JavaScript (javascript)
Output:
3.14
Code language: CSS (css)
Notice that the decimal context precision only affects the arithmetic operation, not the Decimal
constructor. For example:
import decimal
from decimal import Decimaldecimal.getcontext().prec = 2
pi = Decimal('3.14159')
radius = 1
print(pi)
area = pi * radius * radius
print(area)
Code language: JavaScript (javascript)
When you use a float that doesn’t have an exact binary float representation, the Decimal
constructor cannot create an accurate decimal representation. For example:
import decimal
from decimal import Decimalx = Decimal(0.1)
print(x)
Code language: JavaScript (javascript)
Output:
0.1000000000000000055511151231257827021181583404541015625
Code language: CSS (css)
In practice, you’ll use a string or a tuple to construct a Decimal
.
Decimal arithmetic operations
Some arithmetic operators don’t work the same as floats or integers, such as div (//
) and mod (%)
.
For decimal numbers, the //
operator performs a truncated division:
x // y = trunc( x / y)
Code language: JavaScript (javascript)
The Decimal
class provides some mathematical operations such as sqrt
and log
. However, it doesn’t have all the functions defined in the math
module.
When you use functions from the math
module for decimal numbers, Python will cast the Decimal
objects to floats before carrying arithmetic operations. This results in losing the precision built in the decimal objects.
Summary
- Use the Python
decimal
module when you want to support fast correctly-rounded decimal floating-point arithmetic. - Use the
Decimal
class from thedecimal
module to create Decimal object from strings, integers, and tuples. - The
Decimal
numbers have a context that controls the precision and rounding mechanism. - The
Decimal
class doesn’t have all methods defined in themath
module. However, you should use the Decimal’s arithmetic methods if they’re available.