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.
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). |
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
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.
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
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
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!
The C implementation of math.fsum() uses Jonathan Shewchuk's famous adaptive-precision summation algorithm (1997):
math.fsum() maintains an array of partial sums called an expansion.math.fsum() compress the expansion into a single IEEE 754 float, performing exactly one rounding operation at the very end.| 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 |
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
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():
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!")
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.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.