Python math.fsum(): High-Precision Floating-Point Summation

The math.fsum(iterable) function computes the sum of all elements in an iterable while maintaining full floating-point precision. Unlike Python's built-in sum(), which loses precision through intermediate rounding errors (for example, sum([0.1]*10) == 0.9999999999999999), math.fsum() uses Shewchuk's algorithm to track multiple intermediate partial sums, guaranteeing the exact result math.fsum([0.1]*10) == 1.0.

Return Type: Always returns a float. Calling math.fsum([]) on an empty iterable returns 0.0.

Syntax & Parameters 🔝

The math.fsum() function is part of Python's standard math module:

import math

math.fsum(iterable)
Parameter Type Description
iterable Iterable Required. Any iterable containing numbers: list, tuple, set, range, generator, or array.
Return Value float The accumulated sum of the elements as a 64-bit IEEE 754 float.
TypeError Exception Raised when passing a non-iterable (e.g. integer) or an iterable containing non-numeric elements (e.g. strings).

Basic Usage with Diverse Iterables 🔝

math.fsum() accepts any Python iterable, automatically converting integers and floats into a consolidated float total:

import math

# 1. Summing a list of integers and floats
num_list = [10, 20.5, 30, 40.25]
print("List sum:     ", math.fsum(num_list))   # Output: 100.75

# 2. Summing a tuple
num_tuple = (1.1, 2.2, 3.3)
print("Tuple sum:    ", math.fsum(num_tuple))  # Output: 6.6

# 3. Summing a set (distinct values)
num_set = {5, 10, 15}
print("Set sum:      ", math.fsum(num_set))    # Output: 30.0

# 4. Summing a range object
print("Range sum:    ", math.fsum(range(1, 11))) # Output: 55.0

# 5. Summing a generator expression (memory efficient)
squares_gen = (x**2 for x in range(1, 6))
print("Generator sum:", math.fsum(squares_gen)) # Output: 55.0

# 6. Empty iterable returns 0.0
print("Empty sum:    ", math.fsum([]))           # Output: 0.0

Output:

List sum:      100.75
Tuple sum:     6.6
Set sum:       30.0
Range sum:     55.0
Generator sum: 55.0
Empty sum:     0.0

Why math.fsum() Beats sum(): Precision & Catastrophic Cancellation 🔝

The primary motivation for math.fsum() is preventing floating-point rounding errors. Python's built-in sum() adds numbers sequentially: accum = accum + x. At every addition step, IEEE 754 rounds the result to 53 bits of precision, discarding low-order bits.

Scenario 1: Accumulation of Repeated Decimals

In base-2 binary floating point, the decimal 0.1 cannot be represented finitely. Repeated sequential additions compound rounding noise:

import math

tenths = [0.1] * 10

print("Built-in sum():  ", sum(tenths))        # 0.9999999999999999
print("math.fsum():     ", math.fsum(tenths))   # 1.0 (Exact!)
print("sum() == 1.0?     ", sum(tenths) == 1.0)
print("math.fsum() == 1.0?", math.fsum(tenths) == 1.0)

Output:

Built-in sum():   0.9999999999999999
math.fsum():      1.0
sum() == 1.0?     False
math.fsum() == 1.0? True

Scenario 2: Catastrophic Cancellation

Catastrophic cancellation occurs when numbers with huge magnitude differences cancel each other out. With naive summation, adding a small number to a massive number drops the small number entirely off the precision register:

import math

# 1e100 and -1e100 cancel out, leaving exactly 1.0
values = [1e100, 1.0, -1e100]

print("Built-in sum():", sum(values))       # Evaluates: (1e100 + 1.0) - 1e100 = 0.0 !
print("math.fsum():   ", math.fsum(values))  # Correctly tracks 1.0 !

Output:

Built-in sum(): 0.0
math.fsum():    1.0
Why did sum() produce 0.0? When sum() added 1.0 to 1e100, the difference in exponents exceeded 53 bits. The 1.0 was completely truncated. Then subtracting 1e100 produced 0.0. math.fsum() avoids this by maintaining separate non-overlapping precision buckets!

How Shewchuk's Algorithm Works 🔝

The C implementation of math.fsum() uses Jonathan Shewchuk's famous adaptive-precision summation algorithm (1997):

  • Instead of maintaining a single running float total, math.fsum() maintains an array of partial sums called an expansion.
  • When each new number is added, error terms (the bits that would normally be discarded during rounding) are captured and saved into lower-order buckets in the expansion.
  • Only after every number in the iterable has been processed does math.fsum() compress the expansion into a single IEEE 754 float, performing exactly one rounding operation at the very end.

math.fsum() vs. sum() vs. numpy.sum() 🔝

Feature math.fsum() Built-in sum() numpy.sum()
Algorithm Shewchuk adaptive expansion Naive sequential accumulation Pairwise summation
Precision Full precision (single round at end) Subject to cumulative rounding error Better than sum(), but less exact than fsum
Return Type Always float Preserves type (e.g. int) Preserves NumPy dtype
Speed on Lists Fast C implementation Fast C implementation Slow on Python lists; fast on NumPy arrays
Primary Use Case Financial ledgers, physics, science General integer loops & counts Massive vectorized numeric tensors

Special Values: Infinities and NaNs 🔝

math.fsum() adheres strictly to IEEE 754 arithmetic standards when encountering special floating-point numbers:

import math

# Adding infinity to finite numbers yields infinity
print("fsum([1.0, 2.0, inf]): ", math.fsum([1.0, 2.0, float('inf')]))

# Opposing infinities (+inf and -inf) produce NaN (indeterminate form)
print("fsum([inf, -inf]):      ", math.fsum([float('inf'), float('-inf')]))

# Any NaN element propagates to NaN
print("fsum([10.0, nan, 5.0]):  ", math.fsum([10.0, float('nan'), 5.0]))

Output:

fsum([1.0, 2.0, inf]):  inf
fsum([inf, -inf]):      nan
fsum([10.0, nan, 5.0]):  nan

Hands-on Exercise: High-Volume Financial Ledger Auditor with Assertions 🔝

In financial banking and cryptocurrency clearinghouses, transactions span orders of magnitude: massive institutional transfers (e.g. $10,000,000.00) interleave with millions of retail micro-transactions ($0.10, $0.05). If an accounting reconciliation system uses naive summation, cumulative rounding errors falsely trigger fraud or imbalance alerts.

Let's build an audit function that verifies whether an accounting ledger balances to zero, comparing naive sum() against math.fsum():

Python Exercise: Precision Financial Ledger Auditor
import math

def audit_ledger_balance(transactions, tolerance=1e-12):
    """
    Audits a stream of debit and credit transactions.
    Returns (is_balanced, fsum_total, naive_sum_total).
    """
    if not isinstance(transactions, (list, tuple)):
        raise TypeError("Transactions must be a list or tuple.")
    
    exact_total = math.fsum(transactions)
    naive_total = sum(transactions)
    balanced = math.isclose(exact_total, 0.0, abs_tol=tolerance)
    
    return (balanced, exact_total, naive_total)

# Test Case: 10 small deposits of $0.10 offset by a single $1.00 withdrawal
balanced_batch = [0.1] * 10 + [-1.0]
is_bal, f_tot, n_tot = audit_ledger_balance(balanced_batch)

print(f"Batch 1 Balanced? {is_bal} | fsum: {f_tot} | naive sum: {n_tot}")

# Unit Assertions
assert is_bal is True
assert f_tot == 0.0
assert n_tot != 0.0  # Naive sum fails (-1.1102e-16)!

# Massive institutional offset batch
large_batch = [50000000.0, 0.07, -50000000.0, -0.07]
is_bal2, f_tot2, _ = audit_ledger_balance(large_batch)
assert is_bal2 is True
assert f_tot2 == 0.0

print("All financial ledger audit assertions passed successfully!")

Practice in Google Colab & GitHub 🔝

Open in Google Colab View on GitHub

Run all math.fsum() precision tests interactively, reproduce catastrophic cancellation benchmarks, compare with sum(), and audit financial transaction ledgers 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