This guide will introduce you to the Python decimal library and class – Python tools which allow you to accurately work with decimal numbers without the rounding and accuracy issues which frequently arise when working with floating point numbers.

## Computers Can’t Perform Accurate Arithmetic With Decimal Numbers

Decimal numbers are commonly stored as floating point number values. Computers are *not* particularly good at performing arithmetic with this type of numbers accurately.

We break down why this is in our Ultimate Beginners Guide to Floating Point Math/Arithmetic Precision Problems.

## The Python Decimal Library

The Python decimal library ships with Python itself, and once imported, provides a convenient way to accurately represent and manipulate decimal numbers.

## Demonstrating the Python Decimal Class

The decimal library contains the *Decimal* class – a variable class which can be used to represent a correctly rounded decimal number in Python.

Consider the following Python code which adds two decimal numbers:

print(0.1 + 0.7)

It *should* print the value:

0.8

But instead it prints:

0.7999999999999999

…due to the rounding accuracy issues noted above.

By Using the Decimal class, this issue is avoided:

# Import the decimal library import decimal # Import the decimal class from decimal import Decimal # Create two decimal class variables for the numbers before adding them print(Decimal('0.1') + Decimal('0.7'))

This will return:

0.8

## How to Use the Decimal Class

To perform calculations with Decimal objects, they need to first be created.

The syntax for creating a Python object using the decimal class is as follows:

new Decimal(value)

Where **value** can be an integer or floating number, string, or tuple value.

### Don’t Create Decimal Objects from Floating PointNumbers

Passing a floating point number to the decimal constructor completely invalidates the purpose of the decimal class, as the floating point number must first be converted to decimal, which brings up the binary accuracy issue outlined above:

num = Decimal(0.1) print(num)

The decimal object assigned to the variable **num** will have the inaccurate value:

0.1000000000000000055511151231257827021181583404541015625

### Instead, Create Decimal Objects from Strings and Tuples

The decimal constructor can be used with strings containing numerical values. This is the easiest to read and use method of initializing decimal values without running into any precision issues:

num = Decimal('0.1') print(num)

The decimal value of the **num** variable will be:

0.1

*Nice and accurate!*

A tuple can also be used to create a decimal object. This is a bit harder to understand, but is useful in some data crunching scenarios where you want to be *really* specific about the of decimal value you are creating:

The tuple used in the decimal constructor should have 3 components – **a sign** (0 for positive or 1 for negative), a **tuple of digits**, and an **integer exponent**:

new Decimal(sign, (digit1, digit2, ...), exponent)

For example:

Decimal((0, (2, 4, 1, 5), -3))

Is the same as:

returns Decimal('2.415')

And creates a decimal object with the value:

2.415

## Working with Decimal Contexts and Setting the Rounding Mechanism

Each Decimal class object is created within a *context*.

These *contexts* control the precision used when rounding numbers, and the method by which rounding occurs.

### Setting the Context

Decimal contexts can be created and set using the Context constructor.

# Import the decimal library import decimal # Import the decimal class from decimal import Decimal, Context # Create a context which rounds to 20 decimal places, using the ROUND_HALF_UP method myContext = Context(prec=20, rounding=decimal.ROUND_HALF_UP) decimal.setcontext(myContext) print(Decimal(1) / Decimal(9))

### Controlling the Precision

The precision should be an integer from **0 to 28** which specifies the number of digits after the decimal which number should use when rounded.

It is set using the **context.prec** value.

### Controlling the Rounding Method

The following rounding methods are available:

Rounding Method | |
---|---|

ROUND_UP | Round away from zero |

ROUND_DOWN | Round towards zero |

ROUND_CEILING | Round towards infinity |

ROUND_FLOOR | Round towards negative infinity |

ROUND_HALF_UP | Round to nearest with ties away from zero |

ROUND_HALF_DOWN | Round to nearest with ties towards zero |

ROUND_HALF_EVEN | round to nearest with ties to nearest even integer |

ROUND_05UP | Round away from zero if last digit after rounding towards zero would have been 0 or 5 – otherwise round towards zero |

…and can be set using the **context.rounding** value.

### Viewing the Current Context

You can see what the values are being currently used when working with the decimal class by viewing the context:

import decimal context = decimal.getcontext() print(context.prec) print(context.rounding)

### Viewing the Default Context

The details for the default context used by the decimal class can be accessed in the same way:

import decimal context = decimal.DefaultContext print(context.prec) print(context.rounding)

Which will output:

28 ROUND_HALF_EVEN

### Changing the Default Context

The default context for the current script can be altered, which will also apply to any further Python processes created:

import decimal decimal.DefaultContext.prec = 6 decimal. DefaultContext.rounding = decimal.ROUND_DOWN

## Rounding

The rounding method set in the decimal class context affects how numbers using that context are rounded.

import decimal from decimal import Decimal context = decimal.getcontext() num = Decimal('1.15') print(round(num, 1)) # Change the rounding method context.rounding = decimal.ROUND_HALF_DOWN print(round(num, 1))

This code will output:

1.2 1.1

…as you can see, the same number variable **num** is rounded, then the rounding method is changed in the context, then it is rounded again, producing different rounded values.

## Performing Accurate Arithmetic with the Decimal Class

When you use Python’s built in math library, decimal objects will be converted to floating point numbers before any calculations are performed – resulting in a loss of precision – so watch out!

Otherwise, the standard arithmetic operators in Python can be used with decimal objects safely:

import decimal from decimal import Decimal print(Decimal('0.3') / Decimal('0.2') + Decimal('3'))

The above code will output:

4.5

Which is the correct answer, with the correct precision.