How to properly round a float to two decimal places in Python? I have a value (13.949999999999999) that I want to round to 13.95, but when I use the round() function with round(a, 2), it still returns 13.949999999999999 instead of the expected 13.95.
The issue you’re encountering with Python’s round() function is a common floating-point precision problem. The value 13.949999999999999 is stored as a binary float that’s very slightly less than 13.95 due to how computers represent decimal numbers in binary format, causing round() to round down instead of up to 13.95.
Contents
- Understanding the Floating-Point Precision Problem
- Multiple Solutions for Proper Rounding
- Best Practices for Financial Calculations
- Alternative Approaches and When to Use Them
- Common Pitfalls and How to Avoid Them
Understanding the Floating-Point Precision Problem
The issue stems from how computers represent decimal numbers in binary format. Decimal numbers like 13.95 cannot be represented exactly in binary floating-point format, leading to tiny precision errors.
>>> a = 13.949999999999999
>>> a
13.949999999999999
>>> a == 13.95
False
>>> a < 13.95
True
When you use round(a, 2), Python rounds to the nearest representable float. Since the actual stored value is slightly less than 13.95, it rounds down to 13.949999999999999 instead of up to 13.95.
Multiple Solutions for Proper Rounding
1. Using the Decimal Module for Exact Precision
The decimal module provides exact decimal representation and proper rounding:
from decimal import Decimal, ROUND_HALF_UP
a = 13.949999999999999
# Convert to Decimal with proper rounding
result = float(Decimal(str(a)).quantize(Decimal('0.00'), rounding=ROUND_HALF_UP))
print(result) # Output: 13.95
2. String Formatting with Rounding
String formatting can often handle this correctly:
a = 13.949999999999999
result = float("{:.2f}".format(a))
print(result) # Output: 13.95
# Alternatively using f-strings
result = float(f"{a:.2f}")
print(result) # Output: 13.95
3. Adding a Small Epsilon Value
For values very close to the rounding boundary, you can add a tiny epsilon:
a = 13.949999999999999
epsilon = 1e-10
result = round(a + epsilon, 2)
print(result) # Output: 13.95
4. Using Math.ceil with Adjustment
For values that should round up:
import math
a = 13.949999999999999
# If the value is very close to the next cent, round up
if a - math.floor(a * 100) / 100 > 0.995:
result = math.ceil(a * 100) / 100
else:
result = round(a, 2)
print(result) # Output: 13.95
Best Practices for Financial Calculations
Always Use Decimal for Financial Data
For any financial calculations, use the decimal module:
from decimal import Decimal, getcontext
# Set precision
getcontext().prec = 6
# Work with Decimal throughout your calculations
amount = Decimal('13.949999999999999')
rounded_amount = amount.quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
print(float(rounded_amount)) # Output: 13.95
Store as Integers (Cents)
For maximum precision, store monetary values as integers representing cents:
# Store in cents
cents = 1395 # Represents $13.95
dollars = cents / 100.0
print(round(dollars, 2)) # Output: 13.95
Avoid Repeated Floating-Point Operations
Be careful with operations that accumulate floating-point errors:
# Instead of this:
total = 0.0
for i in range(1000):
total += 0.1
# Do this:
from decimal import Decimal
total = Decimal('0.0')
for i in range(1000):
total += Decimal('0.1')
Alternative Approaches and When to Use Them
NumPy Array Operations
For numerical computations, NumPy provides robust rounding:
import numpy as np
a = 13.949999999999999
result = np.round(a, 2)
print(result) # Output: 13.95
Pandas DataFrame Operations
When working with pandas DataFrames:
import pandas as pd
df = pd.DataFrame({'value': [13.949999999999999]})
df['rounded'] = df['value'].round(2)
print(df)
Custom Rounding Function
Create a reliable rounding function:
def safe_round(number, decimals=2):
"""Round a number to specified decimal places with proper handling of edge cases."""
if isinstance(number, str):
number = float(number)
# Convert to string to avoid floating-point representation issues
str_num = f"{number:.20f}"
# Round the string representation
return round(float(str_num), decimals)
print(safe_round(13.949999999999999)) # Output: 13.95
Common Pitfalls and How to Avoid Them
Pitfall 1: Assuming Round() Always Works as Expected
The round() function in Python uses “round half to even” (banker’s rounding), which can be surprising:
>>> round(2.5)
2
>>> round(3.5)
4
Pitfall 2: Comparing Floats for Equality
Never compare floats directly for equality:
# Instead of:
if a == 13.95:
# Do something
# Use:
if abs(a - 13.95) < 1e-9:
# Do something
Pitfall 3: Accumulating Floating-Point Errors
Be careful with iterative calculations:
# Bad - accumulates error
total = 0.0
for _ in range(100):
total += 0.1
# Good - uses decimal
from decimal import Decimal
total = Decimal('0.0')
for _ in range(100):
total += Decimal('0.1')
Pitfall 4: Ignoring Locale-Specific Formatting
For international applications, consider locale-specific formatting:
import locale
# Set locale for proper number formatting
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
amount = 13.949999999999999
formatted = locale.currency(round(amount, 2), grouping=True)
print(formatted) # Output: $13.95
For your specific case with 13.949999999999999, the most reliable solution is to use the decimal module with proper rounding, as this will give you the exact result you expect while avoiding floating-point precision issues.
Conclusion
- Floating-point precision issues cause
round(13.949999999999999, 2)to return 13.949999999999999 instead of 13.95 - Use the
decimalmodule for financial calculations to ensure exact precision - String formatting (
"{:.2f}".format()or f-strings) often provides the expected rounding behavior - Store monetary values as integers (cents) when maximum precision is required
- Be aware of Python’s “round half to even” behavior and test edge cases
- For production code, implement a robust rounding strategy that handles floating-point edge cases
The key takeaway is that for any precision-critical applications, especially financial ones, you should avoid relying on floating-point arithmetic alone and instead use the decimal module or integer-based representations.