Python Rounding: round(), Banker's Rounding, & the Decimal Module

Python provides multiple mechanisms to round numbers, led by the built-in round(number, ndigits=None) function. Python implements the IEEE 754 standard Banker's Rounding (Round-Half-to-Even): when a number is halfway between two possibilities (e.g. 2.5 or 3.5), it rounds to the nearest even number (round(2.5) == 2 and round(3.5) == 4) to eliminate cumulative statistical bias.

Return Type Nuance: Calling round(x) with ndigits=None returns an int. Passing ndigits ≥ 0 returns a float, while negative ndigits rounds to tens, hundreds, or thousands.

Syntax & Parameters of round() 🔝

The round() function is a core Python built-in and requires no module imports:

rounded_value = round(number, ndigits=None)
Parameter Type Description
number float or int Required. The numeric value to be rounded.
ndigits int or None Optional (default=None). Number of decimal places. Positive rounds decimals; zero rounds to nearest whole float; negative rounds to powers of 10.
Return Value int or float Returns an int if ndigits=None; returns a float if ndigits is an integer (or preserves input type for custom classes).
TypeError Exception Raised when number is non-numeric or ndigits is not an integer.

Basic Usage & Negative ndigits (Tens, Hundreds) 🔝

Passing different values for ndigits controls whether you round to decimals, whole numbers, or orders of magnitude:

# 1. Omitting ndigits returns an int
print("round(12.34):     ", round(12.34), type(round(12.34)))      # 12 <class 'int'>
print("round(12.89):     ", round(12.89), type(round(12.89)))      # 13 <class 'int'>

# 2. Positive ndigits returns a float rounded to decimal places
print("round(12.34567, 2):", round(12.34567, 2))                   # 12.35 <class 'float'>
print("round(12.34567, 4):", round(12.34567, 4))                   # 12.3457

# 3. Negative ndigits rounds to tens, hundreds, and thousands
print("round(1245, -1):   ", round(1245, -1))                     # Nearest 10: 1250
print("round(1245, -2):   ", round(1245, -2))                     # Nearest 100: 1200
print("round(1745, -3):   ", round(1745, -3))                     # Nearest 1000: 2000

Output:

round(12.34):      12 <class 'int'>
round(12.89):      13 <class 'int'>
round(12.34567, 2): 12.35
round(12.34567, 4): 12.3457
round(1245, -1):    1250
round(1245, -2):    1200
round(1745, -3):    2000

The Banker's Rounding Rule (Round-Half-to-Even) 🔝

Most schools teach "round half up" (where any fraction ≥ 0.5 rounds upwards). However, Python follows the IEEE 754 standard known as Banker's Rounding (Round-Half-to-Even):

# Halfway numbers round to the nearest EVEN integer:
print("round(0.5):", round(0.5))  # 0 is even -> 0
print("round(1.5):", round(1.5))  # 2 is even -> 2
print("round(2.5):", round(2.5))  # 2 is even -> 2
print("round(3.5):", round(3.5))  # 4 is even -> 4
print("round(4.5):", round(4.5))  # 4 is even -> 4
print("round(5.5):", round(5.5))  # 6 is even -> 6

Output:

round(0.5): 0
round(1.5): 2
round(2.5): 2
round(3.5): 4
round(4.5): 4
round(5.5): 6
Why Banker's Rounding? If an algorithm always rounds 0.5 upwards, summing thousands of rounded numbers introduces a systematic positive drift (+0.5 bias on average per halfway number). By rounding half towards even, half of the ties round up and half round down, maintaining an unbiased expected mean.

The IEEE 754 Float Precision Trap (Why round(2.675, 2) == 2.67) 🔝

A classic complaint among Python developers is why round(2.675, 2) yields 2.67 instead of 2.68. This is not a bug in Python; it is an inherent artifact of binary floating-point representation:

print("round(2.675, 2):", round(2.675, 2))  # Output: 2.67

# Inspect the exact underlying 53-bit IEEE 754 binary representation:
print(f"Exact representation: {2.675:.25f}")

Output:

round(2.675, 2): 2.67
Exact representation: 2.6749999999999998223643161

Because 2.675 is stored internally as 2.6749999999999998..., the number is strictly less than halfway to 2.68. Consequently, Python correctly rounds down to 2.67.

Exact Financial Rounding with the decimal Module 🔝

For financial accounting and invoicing where statutory regulations require exact decimal arithmetic, use Python's standard decimal module:

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

# Pass strings to Decimal to avoid float binary conversion artifacts
val = Decimal('2.675')

# 1. Traditional School Rounding (Round Half Up)
half_up = val.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
print("ROUND_HALF_UP (School):  ", half_up)    # Output: 2.68

# 2. Exact Banker's Rounding (Round Half Even)
half_even = val.quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN)
print("ROUND_HALF_EVEN (Banker):", half_even)  # Output: 2.68

Output:

ROUND_HALF_UP (School):   2.68
ROUND_HALF_EVEN (Banker): 2.68

Master Comparison: round() vs. floor() vs. ceil() vs. trunc() 🔝

Review how each rounding function behaves across positive, negative, and halfway inputs:

Input round(x) math.floor(x) math.ceil(x) math.trunc(x)
2.2 2 2 3 2
2.5 2 (even) 2 3 2
2.8 3 2 3 2
3.5 4 (even) 3 4 3
−2.5 −2 (even) −3 −2 −2
−2.8 −3 −3 −2 −2

Hands-on Exercise: Financial Tax & Cash POS Rounding with Assertions 🔝

In retail and commerce, systems must handle two different rounding requirements:

  1. Sales Tax Calculation: Regulatory statutes often mandate traditional Half-Up rounding to the nearest cent.
  2. Cash Register Denomination Rounding (Swedish Rounding): In countries without 1-cent coins (e.g. Canada, Australia), cash totals must round to the nearest 5 cents (0.05).
Python Exercise: Regulatory Tax & Cash POS Denomination Rounder
import math
from decimal import Decimal, ROUND_HALF_UP

def calculate_sales_tax(subtotal_dollars, tax_rate_percent):
    """
    Computes exact sales tax with traditional ROUND_HALF_UP rounding.
    """
    if subtotal_dollars < 0 or tax_rate_percent < 0:
        raise ValueError("Subtotal and tax rate must be non-negative.")
        
    sub_dec = Decimal(str(subtotal_dollars))
    rate_dec = Decimal(str(tax_rate_percent)) / Decimal('100')
    raw_tax = sub_dec * rate_dec
    tax_cents = raw_tax.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
    return float(tax_cents)

def swedish_cash_round(amount, step=0.05):
    """
    Rounds a monetary total to the nearest cash denomination (e.g. 0.05).
    """
    if amount < 0 or step <= 0:
        raise ValueError("Amount and step must be positive.")
    # Scale by 1/step, round, then scale back
    units = round(amount / step)
    return round(units * step, 2)

# Unit Assertions
# 1. Tax on $35.45 at 8.25%: 35.45 * 0.0825 = 2.924625 -> $2.92
assert calculate_sales_tax(35.45, 8.25) == 2.92

# 2. Tax on $49.99 at 7.5%: 49.99 * 0.075 = 3.74925 -> $3.75
assert calculate_sales_tax(49.99, 7.5) == 3.75

# 3. Cash rounding checks to nearest 5 cents
assert swedish_cash_round(10.02) == 10.00
assert swedish_cash_round(10.03) == 10.05
assert swedish_cash_round(10.07) == 10.05
assert swedish_cash_round(10.08) == 10.10

print("Sales tax on $100 at 8.875%:", calculate_sales_tax(100.0, 8.875))
print("Cash total for $15.43:       ", swedish_cash_round(15.43))
print("All sales tax & POS rounding assertions passed successfully!")

Practice in Google Colab & GitHub 🔝

Open in Google Colab View on GitHub

Run all rounding examples interactively, test banker's round-half-to-even behavior, explore the Decimal module, and execute cash register denomination rounding in Google Colab.




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer