When you deal with money, bonuses, or ratios in Python, you’ve probably used something like this:

from decimal import Decimal, ROUND_HALF_UP

value = Decimal("1.005")
rounded = value.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
print(rounded)  # 1.01

Seems simple enough, until you realize that Python, by default, doesn’t round this way.

Let’s dig into why.

Understanding the Rounding Problem

Rounding isn’t as innocent as it looks. Take a simple case:

Value Rounded (2 decimals) Expected 1.004 1.00 1.005 1.00 or 1.01?

If you try this in Python without specifying a rounding mode, you’ll get:

from decimal import Decimal

print(Decimal("1.005").quantize(Decimal("0.01")))
# Output: 1.00

Wait, what? Why did 1.005 become 1.00 instead of 1.01?

That’s because Python follows IEEE 754 standards for floating-point arithmetic, and its default rounding mode is ROUND_HALF_EVEN, also called banker’s rounding.

What is ROUND_HALF_EVEN?

ROUND_HALF_EVEN means:

If a number is exactly halfway between two rounding choices, round to the nearest even number.

For example:

Original Rounded (1 decimal) Rule 1.5 2 even 2.5 2 even 3.5 4 even 4.5 4 even

Notice the pattern? Half the time, it rounds up. Half the time, down.

This keeps the total rounding error unbiased when you process large datasets, which is exactly why it’s the default in most languages and hardware implementations.

Why It Matters

Let’s compare two rounding modes across a range of numbers.

The test:

Round every number from 0.5 to 99.5, inclusive.

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

numbers = [Decimal(i) + Decimal("0.5") for i in range(100)]

def rounded_sum(mode):
    return sum(n.quantize(Decimal("1"), rounding=mode) for n in numbers)

print("ROUND_HALF_UP:", rounded_sum(ROUND_HALF_UP))
print("ROUND_HALF_EVEN:", rounded_sum(ROUND_HALF_EVEN))

The result:
Mode    Total Sum After Rounding    Bias
ROUND_HALF_UP   5050    Slight upward bias
ROUND_HALF_EVEN 5000    Balanced / unbiased

So, when you always round 0.5 up, your numbers gradually inflate. That’s fine if you’re showing prices or bonuses to users, not fine if you’re summing a million transactions in a bank’s backend.

When to Use Each Use Case Recommended Mode Reason Displaying money, bonuses, or salary ROUND_HALF_UP Matches human expectation (“.005 → .01”) Statistical / financial backend calculations ROUND_HALF_EVEN Avoids cumulative rounding bias Scientific or analytical datasets ROUND_HALF_EVEN Follows IEEE 754 standard User-facing invoices, reports ROUND_HALF_UP Feels natural to end users Practical Example

Suppose you’re calculating bonuses based on a ratio:

from decimal import Decimal, ROUND_HALF_UP

def calculate_ratio_amount(base_amount, base_bonus, current_amount):
    base_amount = Decimal(base_amount)
    base_bonus = Decimal(base_bonus)
    current_amount = Decimal(current_amount)

    ratio = base_bonus / base_amount
    result = current_amount * ratio
    return result.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)

If your result is 18.445, this function will output:

Decimal('18.45')

Without ROUND_HALF_UP, the default rounding (ROUND_HALF_EVEN) might give you:

Decimal('18.44')

That’s a visible difference in financial reports.

The Key Takeaway

Python’s ROUND_HALF_EVEN isn’t wrong, it’s scientifically fair. But in human-facing systems, fairness and intuition aren’t the same thing.

So, here’s the rule of thumb:

Use ROUND_HALF_UP when humans will read it. Use ROUND_HALF_EVEN when machines will sum it.

Random Note


LRUCache can be implemented - Hashmap + Doubly Linked List - Python Dict/OrderDict - HashMap + List At least I've tested them for fun :p