🕯️ Magic Note
The decimal module is based on the General Decimal Arithmetic Specification, which is used in many financial and scientific systems. It is slower than binary floating point, but for applications where precision matters, it is the right tool.
Python
from decimal import Decimal
# Floating point (binary) – has rounding errors
print(0.1 + 0.2) # 0.30000000000000004
print(0.1 + 0.2 == 0.3) # False
print(1.20 – 1.15) # 0.04999999999999982
# Decimal – exact decimal arithmetic
print(Decimal(“0.1”) + Decimal(“0.2”)) # 0.3
print(Decimal(“0.1”) + Decimal(“0.2”) == Decimal(“0.3”)) # True
print(Decimal(“1.20”) – Decimal(“1.15”)) # 0.05
Python
from decimal import Decimal
# From string (recommended)
d1 = Decimal(“0.1”)
d2 = Decimal(“3.141592653589793”)
d3 = Decimal(“123.45”)
# From integer
d4 = Decimal(42)
# From tuple (sign, digits, exponent)
d5 = Decimal((0, (1, 2, 3, 4, 5), -2)) # 123.45
# Avoid: creating from float (may inherit floating point error)
d6 = Decimal(0.1) # Not exactly 0.1! It is 0.100000000000000005551…
print(d6) # Decimal(‘0.1000000000000000055511151231257827021181583404541015625’)
# Always create from string for exact representation
d7 = Decimal(“0.1”) # Exactly 0.1
🕯️ Magic Note
Creating a Decimal from a float defeats the purpose. The float already has rounding error. That error becomes part of the Decimal. Always use strings for exact decimal values.
Python
from decimal import Decimal
a = Decimal(“10.50”)
b = Decimal(“3.25”)
print(f”Addition: {a + b}”) # 13.75
print(f”Subtraction: {a – b}”) # 7.25
print(f”Multiplication: {a * b}”) # 34.1250
print(f”Division: {a / b}”) # 3.230769230769230769230769231 (default precision)
print(f”Floor division: {a // b}”) # 3
print(f”Modulus: {a % b}”) # 0.75
print(f”Power: {a ** 2}”) # 110.25
# Comparison operators work as expected
print(a > b) # True
print(a == b) # False
Python
from decimal import Decimal, getcontext
# Check current precision (default is 28)
print(f”Default precision: {getcontext().prec}”)
# Change precision
getcontext().prec = 6
print(Decimal(“1”) / Decimal(“7”)) # 0.142857 (6 digits of precision)
getcontext().prec = 10
print(Decimal(“1”) / Decimal(“7”)) # 0.1428571429 (10 digits)
getcontext().prec = 28 # Reset to default
| Rounding Mode | Behavior |
|---|---|
| ROUND_CEILING | Round towards Infinity (up for positive, up for negative) |
| ROUND_FLOOR | Round towards -Infinity (down for positive, down for negative) |
| ROUND_UP | Round away from zero |
| ROUND_DOWN | Round towards zero |
| 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 (banker’s rounding) |
| ROUND_05UP | Round away from zero if last digit is 0 or 5 |
Python
from decimal import Decimal, getcontext, ROUND_HALF_UP, ROUND_HALF_DOWN, ROUND_CEILING, ROUND_FLOOR
value = Decimal(“0.125”)
# quantize() rounds to specified decimal places
print(f”Original: {value}”)
print(f”ROUND_HALF_UP: {value.quantize(Decimal(‘0.01’), rounding=ROUND_HALF_UP)}”) # 0.13
print(f”ROUND_HALF_DOWN: {value.quantize(Decimal(‘0.01’), rounding=ROUND_HALF_DOWN)}”) # 0.12
negative = Decimal(“-0.125”)
print(f”Negative: {negative}”)
print(f”ROUND_CEILING: {negative.quantize(Decimal(‘0.01’), rounding=ROUND_CEILING)}”) # -0.12 (up for negative)
print(f”ROUND_FLOOR: {negative.quantize(Decimal(‘0.01’), rounding=ROUND_FLOOR)}”) # -0.13 (down for negative)
# For financial applications, ROUND_HALF_EVEN is often used (reduces bias)
from decimal import ROUND_HALF_EVEN
print(f”ROUND_HALF_EVEN: {Decimal(‘0.125’).quantize(Decimal(‘0.01’), rounding=ROUND_HALF_EVEN)}”) # 0.12
print(f”ROUND_HALF_EVEN: {Decimal(‘0.135’).quantize(Decimal(‘0.01’), rounding=ROUND_HALF_EVEN)}”) # 0.14
🕯️ Magic Note
Banker’s rounding (ROUND_HALF_EVEN) rounds .5 to the nearest even digit. This eliminates the upward bias of always rounding .5 up. It is the default rounding for many financial systems and for Python’s round() function when dealing with ties.
Python
from decimal import Decimal, ROUND_HALF_UP
price = Decimal(“19.999”)
tax = Decimal(“0.075”) # 7.5% tax
total = price * (1 + tax)
print(f”Raw total: {total}”) # 21.498925
# Round to 2 decimal places (currency)
rounded = total.quantize(Decimal(“0.01”), rounding=ROUND_HALF_UP)
print(f”Rounded total: {rounded}”) # 21.50
# Quantize to different precisions
value = Decimal(“123.4567”)
print(value.quantize(Decimal(“1”))) # 123
print(value.quantize(Decimal(“0.1”))) # 123.5
print(value.quantize(Decimal(“0.01”))) # 123.46
print(value.quantize(Decimal(“0.001”))) # 123.457
Python
from decimal import Decimal, ROUND_HALF_UP, getcontext
# Set high precision for intermediate calculations
getcontext().prec = 28
class Money:
def __init__(self, amount):
if isinstance(amount, Decimal):
self.amount = amount
else:
self.amount = Decimal(str(amount))
def __add__(self, other):
result = self.amount + other.amount
return Money(result.quantize(Decimal(“0.01”), rounding=ROUND_HALF_UP))
def __sub__(self, other):
result = self.amount – other.amount
return Money(result.quantize(Decimal(“0.01”), rounding=ROUND_HALF_UP))
def __mul__(self, factor):
result = self.amount * Decimal(str(factor))
return Money(result.quantize(Decimal(“0.01”), rounding=ROUND_HALF_UP))
def __str__(self):
return f”${self.amount:.2f}”
# Usage
price = Money(19.99)
tax_rate = Decimal(“0.075”)
tax = price * tax_rate
total = price + tax
print(f”Price: {price}”) # $19.99
print(f”Tax: {tax}”) # $1.50
print(f”Total: {total}”) # $21.49
# Bulk purchase with discount
quantity = 3
subtotal = price * quantity
discount = Money(5.00)
final = subtotal – discount
print(f”3 items: {subtotal}”) # $59.97
print(f”After $5 discount: {final}”) # $54.97
| Use Decimal When... | Use Float When... |
|---|---|
| Financial and currency calculations | Scientific and engineering calculations |
| You need exact decimal representation | Speed is critical (float is much faster) |
| Accounting, banking, tax computations | 3D graphics, game physics |
| When rounding behavior must be controlled | When working with very large or very small numbers |
| When comparing decimal numbers for equality | When memory is limited (float uses less) |
| Numbers that represent precise human values (money, measurements) | Performance-sensitive applications |
Python
# Performance comparison (conceptual)
# Float operations: very fast (hardware accelerated)
# Decimal operations: slower (software implementation)
# But for financial code, correctness matters more than speed
price = Decimal(“19.99”) # Correct
# price = 19.99 # Dangerous for money
Python
from decimal import Decimal, getcontext, ROUND_HALF_UP
# Create from string (most common)
d = Decimal(“123.45”)
# Basic arithmetic
d + Decimal(“10.00”)
d – Decimal(“5.00”)
d * Decimal(“2”)
d / Decimal(“3”)
# Round to 2 decimal places
rounded = d.quantize(Decimal(“0.01”), rounding=ROUND_HALF_UP)
# Change global precision
getcontext().prec = 50
# Square root
sqrt = d.sqrt()
# Compare
if d < Decimal(“100”):
print(“Less than 100”)
# Convert to string for output
output = str(d)
- Creating Decimal from float: Decimal(0.1) does not give Decimal(“0.1”)
- Mixing Decimal and float in operations (TypeError)
- Forgetting to quantize currency to 2 decimal places
- Using Decimal when performance is critical and exactness is not needed
- Not setting precision high enough for intermediate calculations
- Assuming Decimal is always slower (it is, but correctness first)
- Why should you never use float for currency?
- How do you create a Decimal representing exactly 0.1?
- Write code to calculate 10% tax on $19.99 and round to 2 decimal places.
- What does quantize(Decimal(“0.01”)) do?
- What is banker’s rounding (ROUND_HALF_EVEN) and why is it used?
- How do you change the global precision for Decimal operations?
⚡ Whisper
The float is fast. It is the workhorse of scientific computing, graphics, and machine learning. But for money, the float is a liar. It says 0.1 + 0.2 == 0.3 is False. It turns 1.20 – 1.15 into 0.04999999999999982. These are small lies. But lies compound. In accounting, small errors become large discrepancies. Auditors notice. Customers complain. The decimal module tells the truth. It computes exactly. It rounds predictably. It handles money honestly. Use it for financial code. Use it for tax calculations. Use it for any situation where human decimal expectations matter. The performance cost is worth it. Your customers deserve correct balances. Your accountants deserve exact numbers. In the world of money, truth matters. Use Decimal.