How To Stop Floating Point Arithmetic Errors in Python

Learn to use the Decimal library

How To Stop Floating Point Arithmetic Errors in Python
Photo by Greg Nunes on Unsplash

We expect precision, consistency, and accuracy when we code. After all, it’s a computer doing the work. But your arithmetic may have been off the entire time and you didn’t even know.

If you’ve experienced floating point arithmetic errors, then you know what we’re talking about. If you’re unsure what that means, let’s show instead of tell.print(1.1 * 3) # 3.3000000000000003

This happens because decimal values are actually stored as a formula and do not have an exact representation.

We’re going to go over a solution to these inconsistencies, using a natively available library called Decimal.

Learn to Use the Decimal Library

According to the official Python documentation:

The decimal module provides support for fast correctly-rounded decimal floating point arithmetic.

So how do we go about using this readily available tool? Let’s start by importing the library. There are multiple components to import so we’ll use the * symbol.from decimal import *

Next, we’ll use the Decimal() constructor with a string value to create a new object and try our arithmetic again.print(Decimal('1.1') * 3) # 3.3

Make sure to use a string value, because otherwise the floating point number 1.1 will be converted to a Decimal object, effectively preserving the error and probably compounding it even worse than if floating point was used.print(Decimal(1.1) * 3) # 3.300000000000000266453525910


The Golden Rule

You can basically use the decimal objects as you would any other numeric value. However, there is one golden rule we have for those who choose to adopt the decimal library: do not mix and match decimal with float.

If you treat floats and decimals as interchangeable, then you’re likely to run into errors. The two data types are incompatible when it comes to arithmetic.a = Decimal('1.1')
b = 2.2
c = a + b
# TypeError: unsupported operand type(s) for +: 'decimal.Decimal' and 'float'

Beyond this golden rule, here are some tips and tricks for using Decimal().

Use .quantize() for rounding

Pass a decimal object with the appropriate number of decimal places. This is helpful when working with currency. In our example we’ll round a value to two decimal places.a = Decimal('1.123456789')
b = a.quantize(Decimal('1.00'))
print(b) # 1.12

Use getcontext() to set precision

The decimal precision can be customized by modifying the default context. First let’s look at the default context then demonstrate what happens when we make modifications.from decimal import *print(getcontext())
"""
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
"""print(Decimal(1)/Decimal(3))
# 0.3333333333333333333333333333getcontext().prec = 4print(Decimal(1)/Decimal(3))
# 0.3333

Watch out for negative modulus

The modulus operator (%) returns the remainder of a division operation. Normally, the sign of the divisor is preserved when using a negative number.print((-7) % 4) # 1
print(7 % (-4)) # -1

However, the sign of the numerator is preserved with a decimal object.print(Decimal(-7) % 4) # -3
print(7 % Decimal(-4)) # 3


Thanks for reading. Please share your experiences, questions, and comments below!

Subscribe to Dreams of Fortunes and Cookies

Sign up now to get access to the library of members-only issues.
Jamie Larson
Subscribe