NeuroAgent

Complete Guide to Rounding Floats in Python

Learn how to properly round floats in Python despite floating-point precision issues. Discover multiple solutions including decimal module, string formatting, and best practices for financial calculations.

Question

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.

NeuroAgent

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

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.

python
>>> 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:

python
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:

python
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:

python
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:

python
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:

python
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:

python
# 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:

python
# 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:

python
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:

python
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:

python
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:

python
>>> round(2.5)
2
>>> round(3.5)
4

Pitfall 2: Comparing Floats for Equality

Never compare floats directly for equality:

python
# 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:

python
# 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:

python
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 decimal module 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.

Sources

  1. Python documentation - Floating Point Arithmetic: Issues and Limitations
  2. Python decimal module documentation
  3. IEEE 754 floating-point standard