Loading content...
On June 4, 1996, the European Space Agency's Ariane 5 rocket exploded 37 seconds after launch. The cause: an integer overflow. A 64-bit floating-point number representing horizontal velocity was converted to a 16-bit signed integer. The velocity value was too large for 16 bits. The conversion failed. The backup system failed identically. Both systems shut down. The rocket veered off course and self-destructed. Cost: $370 million.
Integer overflow is the most dangerous type of algorithmic bug. It's dangerous precisely because it's silent. There's no exception. No error message. The program continues running with a corrupted value, potentially passing all test cases while hiding a ticking time bomb.
The Nature of the Beast
Unlike off-by-one errors that typically manifest immediately in test results, overflow bugs often work perfectly for small inputs and only fail when values exceed certain thresholds—thresholds that may never be hit during testing but are hit in production with real-world data.
This page provides a deep understanding of integer overflow: why it happens, where it lurks in algorithms, how to detect it, and how to prevent it. You'll learn to think defensively about numeric limits and build overflow-safe code as second nature.
To understand overflow, you must first understand how computers store integers.
Fixed-Width Integer Types
In most programming languages, integers have a fixed number of bits. More bits can represent a larger range of values:
| Type | Bits | Unsigned Range | Signed Range |
|---|---|---|---|
| 8-bit | 8 | 0 to 255 | -128 to 127 |
| 16-bit | 16 | 0 to 65,535 | -32,768 to 32,767 |
| 32-bit | 32 | 0 to ~4.3 billion | ~-2.1 to ~2.1 billion |
| 64-bit | 64 | 0 to ~18 quintillion | ~-9.2 to ~9.2 quintillion |
The Critical Values to Memorize
For 32-bit signed integers (the default in many contexts):
For 64-bit signed integers:
| Language | int/Integer Type | Size | Max Value |
|---|---|---|---|
| C/C++ | int | Usually 32-bit | ~2.1 billion |
| C/C++ | long long | At least 64-bit | ~9.2 quintillion |
| Java | int | Exactly 32-bit | 2,147,483,647 |
| Java | long | Exactly 64-bit | ~9.2 quintillion |
| JavaScript | Number (safe integer) | 53-bit mantissa | 9,007,199,254,740,991 |
| Python 3 | int | Arbitrary precision | Limited only by memory |
| Go | int | Platform-dependent | 32-bit or 64-bit |
Python 3's integers have arbitrary precision—they grow as large as memory allows. However, this doesn't make Python immune! Performance degrades with very large integers, and libraries using C extensions (NumPy, pandas) use fixed-width types that can still overflow.
When an arithmetic operation produces a result outside the representable range, different languages behave differently:
Wraparound (Silent Overflow)
In C, C++, and Java, signed integer overflow causes the value to "wrap around" to the opposite end of the range. This is technically undefined behavior in C/C++ for signed integers but practically results in wraparound on most systems.
Example: In 32-bit signed arithmetic:
The result is a valid-looking integer, just completely wrong.
Consequences of Silent Overflow:
a + b > c might be false when it should be true123456789101112131415161718192021222324
import numpy as np # Python's native int has no overflow (arbitrary precision)large = 2**100print(f"Python int: 2^100 = {large}") # Works fine! # But NumPy uses fixed-width typesint32_val = np.int32(2147483647) # Maximum 32-bit signedprint(f"NumPy int32 max: {int32_val}") # Adding 1 causes overflow (wraps around silently)overflowed = np.int32(int32_val + 1)print(f"Max + 1 = {overflowed}") # Prints -2147483648 (wrapped to min!) # This can corrupt calculationsa = np.int32(2000000000)b = np.int32(1000000000)c = np.int32(500000000) # Is a + b > c? Mathematically yes (3 billion > 500 million)# But with overflow...sum_ab = a + b # Overflows!print(f"a + b = {sum_ab}") # Prints -1294967296 (NEGATIVE!)print(f"Is a + b > c? {sum_ab > c}") # Prints False (WRONG!)The most insidious overflow occurs in intermediate calculations, not final results. Example: (a + b) / 2 might overflow even if the final result is perfectly small. The sum a + b exceeds limits before the division happens!
Certain algorithmic patterns are particularly prone to overflow. Knowing these hot spots helps you apply defensive measures proactively.
(left + right) / 2 can overflow if left and right are large.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
# === BINARY SEARCH MIDPOINT === # DANGEROUS (in languages with fixed integers):def binary_search_overflow_risk(arr, target): left, right = 0, len(arr) - 1 while left <= right: mid = (left + right) // 2 # If left + right > INT_MAX, overflow! # ... # SAFE: Equivalent but avoids sum overflowdef binary_search_safe(arr, target): left, right = 0, len(arr) - 1 while left <= right: mid = left + (right - left) // 2 # Never overflows! # right - left is always non-negative and <= len(arr) # Adding to left is safe # ... # === SUM AND AVERAGE === def average_buggy(arr): """Calculate average - vulnerable to sum overflow.""" total = 0 for x in arr: total += x # In fixed-width: can overflow for large arrays or values return total / len(arr) def average_safe(arr): """Calculate average incrementally to avoid sum overflow.""" if not arr: return 0.0 avg = 0.0 for i, x in enumerate(arr): # Incrementally adjust average: new_avg = old_avg + (x - old_avg)/(i+1) avg += (x - avg) / (i + 1) return avg # This never sums more than two numbers at once! # === FACTORIAL === def factorial_naive(n: int) -> int: """Factorial - overflows very quickly in fixed-width integers.""" result = 1 for i in range(2, n + 1): result *= i # 13! exceeds 32-bit signed, 21! exceeds 64-bit signed return result # For competition problems requiring modular arithmetic:def factorial_modular(n: int, mod: int) -> int: """Factorial modulo 'mod' - prevents overflow.""" result = 1 for i in range(2, n + 1): result = (result * i) % mod # Always stays < mod return result # === COUNT PATHS IN GRID === def count_paths_overflow_risk(m: int, n: int) -> int: """Count paths in m×n grid - sum accumulation can overflow.""" dp = [[1] * n for _ in range(m)] for i in range(1, m): for j in range(1, n): dp[i][j] = dp[i-1][j] + dp[i][j-1] # Sum grows rapidly! return dp[m-1][n-1] # For a 100×100 grid, the number of paths is astronomical! def count_paths_with_modulo(m: int, n: int, mod: int = 10**9 + 7) -> int: """Count paths modulo 'mod' - standard technique.""" dp = [[1] * n for _ in range(m)] for i in range(1, m): for j in range(1, n): dp[i][j] = (dp[i-1][j] + dp[i][j-1]) % mod # Stays bounded return dp[m-1][n-1] # === PRODUCT CALCULATIONS === def product_of_array(arr): """Product of array elements - explodes in size.""" product = 1 for x in arr: product *= x # [2]*64 = 2^64, exceeds 64-bit signed! return product def product_of_array_except_self_safe(arr): """ Product of array except self, handling overflow with log. Uses logarithms to detect overflow before it happens. """ import math # Sum of logs = log of product total_log = sum(math.log(abs(x)) if x != 0 else 0 for x in arr) negative_count = sum(1 for x in arr if x < 0) has_zero = any(x == 0 for x in arr) max_safe_log = math.log(2**62) # Safe 64-bit threshold if total_log > max_safe_log: print("Warning: Product would overflow!") # Proceed with calculation or use modular arithmetic...When given a problem with constraints like 'n ≤ 10⁵' and 'values up to 10⁹', immediately calculate: worst-case sum = 10⁵ × 10⁹ = 10¹⁴, which exceeds 32-bit (~10⁹) but fits in 64-bit (~10¹⁸). This analysis should be your first step.
Different situations call for different prevention strategies. Here are the primary techniques in every programmer's arsenal.
Strategy: When 32-bit integers might overflow, use 64-bit. When 64-bit might overflow, use arbitrary-precision integers or floating-point approximations.
When to Use: This is your first line of defense for most problems.
1234567891011121314151617181920212223242526272829
// C++: Use long long for competition programming#include <iostream>using namespace std; int main() { // DANGEROUS: int can overflow int a = 100000; int b = 100000; int product_int = a * b; // OVERFLOW! Only ~2 billion capacity cout << "int product: " << product_int << endl; // Wrong answer // SAFE: long long handles up to ~9 * 10^18 long long a_safe = 100000LL; // Note the LL suffix long long b_safe = 100000LL; long long product_safe = a_safe * b_safe; cout << "long long product: " << product_safe << endl; // 10,000,000,000 // CAUTION: Even with long long, be careful! // If you forget to cast early, overflow still happens: int x = 100000; int y = 100000; long long wrong = x * y; // x * y computed as int, THEN converted! long long right = (long long)x * y; // x cast first, then product is long long cout << "Wrong: " << wrong << endl; // Overflowed value cout << "Right: " << right << endl; // 10,000,000,000 return 0;}Different languages handle overflow differently. Understanding your language's behavior is critical.
| Language | Default Integer | Overflow Behavior | Protection Available |
|---|---|---|---|
| C/C++ | int (32-bit) | Undefined behavior (usually wraps) | Compiler flags, sanitizers |
| Java | int (32-bit) | Silent wraparound | Math.addExact() throws |
| Python 3 | Arbitrary precision | Never overflows (grows) | N/A (no issue) |
| JavaScript | 53-bit float precision | Precision loss, not wrap | BigInt for true integers |
| Go | Platform-dependent int | Silent wraparound | Manual checks needed |
| Rust | Panic in debug, wrap in release | Configurable | checked_add() etc. |
| C# | int (32-bit) | Silent wrap (default) | checked { } block throws |
12345678910111213141516171819202122232425262728293031
import java.lang.Math; public class SafeArithmetic { public static void main(String[] args) { int a = Integer.MAX_VALUE; int b = 1; // UNSAFE: Silent overflow int unsafeSum = a + b; System.out.println("Unsafe sum: " + unsafeSum); // -2147483648 (wrapped!) // SAFE: Throws ArithmeticException on overflow try { int safeSum = Math.addExact(a, b); System.out.println("Safe sum: " + safeSum); } catch (ArithmeticException e) { System.out.println("Overflow detected!"); } // Other safe methods: // Math.subtractExact(a, b) // Math.multiplyExact(a, b) // Math.incrementExact(a) // Math.decrementExact(a) // Math.negateExact(a) // For computations that shouldn't throw, use long: long safeLong = (long) a + b; System.out.println("Long sum: " + safeLong); // 2147483648 (correct!) }}Rust's explicit methods (checked_add, wrapping_add, saturating_add, overflowing_add) represent best practices. Even if you program in other languages, adopt this mindset: for every arithmetic operation, know whether overflow is possible and what should happen if it occurs.
Let's examine specific algorithms where overflow commonly occurs and how to handle them correctly.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
# === SCENARIO 1: Array Sum Comparisons === # Problem: Is sum of first half greater than sum of second half?# BUGGY approach (in fixed-width languages):def compare_halves_buggy(arr): n = len(arr) mid = n // 2 sum1 = sum(arr[:mid]) # Can overflow sum2 = sum(arr[mid:]) # Can overflow return sum1 > sum2 # Comparison of overflowed values is meaningless! # SAFE approach: Compute difference incrementallydef compare_halves_safe(arr): n = len(arr) mid = n // 2 # Compute sum(first_half) - sum(second_half) directly # This stays bounded: max value is n * max_element difference = 0 for i in range(mid): difference += arr[i] for i in range(mid, n): difference -= arr[i] return difference > 0 # === SCENARIO 2: Checking if Sum Equals Target === # Problem: Does a + b == target?# BUGGY (in fixed-width languages):def sum_equals_target_buggy(a, b, target): return a + b == target # a + b might overflow! # SAFE: Rearrange to avoid sumdef sum_equals_target_safe(a, b, target): return a == target - b # Subtraction of bounded values is safe # (assuming target is a valid value in the range) # === SCENARIO 3: Distance/Magnitude Calculations === # Problem: Euclidean distance between pointsimport math # BUGGY: Squaring can overflowdef euclidean_distance_buggy(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 return math.sqrt(dx * dx + dy * dy) # dx*dx can overflow! # SAFE: Use math.hypot which handles this internallydef euclidean_distance_safe(x1, y1, x2, y2): return math.hypot(x2 - x1, y2 - y1) # math.hypot internally scales to avoid overflow/underflow # For comparison only (no need for actual distance):def is_closer(p1, p2, reference): """Is p1 closer to reference than p2?""" # Use squared distances to avoid sqrt # But be careful: squared distances can overflow even more easily! # For safety in fixed-width, compare component-wise if possible # Or use floating point for the comparison dx1, dy1 = float(p1[0] - reference[0]), float(p1[1] - reference[1]) dx2, dy2 = float(p2[0] - reference[0]), float(p2[1] - reference[1]) dist1_sq = dx1 * dx1 + dy1 * dy1 dist2_sq = dx2 * dx2 + dy2 * dy2 return dist1_sq < dist2_sq # === SCENARIO 4: Running Product for Division Trick === # Problem: For each element, compute product of all OTHER elements# (assuming no zeros for simplicity) # BUGGY: Compute total product, then dividedef product_except_self_buggy(nums): total_product = 1 for x in nums: total_product *= x # OVERFLOW - product grows exponentially! return [total_product // x for x in nums] # SAFE: Prefix and suffix productsdef product_except_self_safe(nums): n = len(nums) result = [1] * n # Prefix products (left to right) prefix = 1 for i in range(n): result[i] = prefix prefix *= nums[i] # Suffix products (right to left) suffix = 1 for i in range(n - 1, -1, -1): result[i] *= suffix suffix *= nums[i] return result # NOTE: Still can overflow if individual prefix/suffix products are huge # For true safety, use modular arithmetic if problem allows, # or use logarithms to check for overflow before computing # === SCENARIO 5: Counting Problems with Large Answers === MOD = 10**9 + 7 # Problem: Count number of ways (e.g., paths, arrangements)def count_arrangements(n: int) -> int: """Example: Count something that grows exponentially.""" if n <= 1: return 1 # dp[i] = number of ways for size i dp = [0] * (n + 1) dp[0] = dp[1] = 1 for i in range(2, n + 1): # Sum over all previous states (hypothetical recurrence) for j in range(i): dp[i] = (dp[i] + dp[j]) % MOD # Always keep bounded return dp[n]Overflow bugs are notoriously hard to detect because they don't cause crashes. They just produce wrong values. Here are strategies for finding them.
-fsanitize=undefined to catch signed overflow at runtime.1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
# === CONSTRAINT ANALYSIS EXAMPLE === def analyze_overflow_risk(problem_constraints: dict) -> None: """ Analyze whether overflow is possible given problem constraints. Example constraints: { 'n': 100000, # Array size 'max_value': 10**9, # Maximum element value 'operation': 'sum' # What we're computing } """ n = problem_constraints['n'] max_val = problem_constraints['max_value'] operation = problem_constraints['operation'] INT32_MAX = 2**31 - 1 INT64_MAX = 2**63 - 1 if operation == 'sum': worst_case = n * max_val print(f"Worst-case sum: {n} × {max_val} = {worst_case:.2e}") print(f" Fits in int32: {worst_case <= INT32_MAX}") print(f" Fits in int64: {worst_case <= INT64_MAX}") elif operation == 'product': # Product of n elements each up to max_val worst_case = max_val ** n print(f"Worst-case product: {max_val}^{n}") print(f" Log10: {n * math.log10(max_val):.1f} (vs int64 max: 18.9)") print(f" WILL OVERFLOW for any fixed-width type!") elif operation == 'sum_of_squares': worst_case = n * (max_val ** 2) print(f"Worst-case sum of squares: {n} × ({max_val})² = {worst_case:.2e}") print(f" Fits in int64: {worst_case <= INT64_MAX}") elif operation == 'pairwise_product_sum': # Sum of all pairwise products: n(n-1)/2 pairs, each up to max_val² num_pairs = n * (n - 1) // 2 worst_case = num_pairs * (max_val ** 2) print(f"Worst-case pairwise product sum: {worst_case:.2e}") print(f" Fits in int64: {worst_case <= INT64_MAX}") # Example usage:import math # Typical competitive programming problemanalyze_overflow_risk({ 'n': 100000, 'max_value': 10**9, 'operation': 'sum'})# Output: Worst-case sum = 10^14, fits in int64 but NOT int32 analyze_overflow_risk({ 'n': 100000, 'max_value': 10**9, 'operation': 'sum_of_squares'})# Output: Worst-case = 10^23, exceeds even int64! # === RUNTIME OVERFLOW DETECTION === def detect_overflow_in_sum(arr, expected_positive=True): """Check if sum computation likely overflowed.""" # In Python, we can safely compute the true sum true_sum = sum(arr) # Simulate 64-bit overflow INT64_MAX = 2**63 - 1 INT64_MIN = -(2**63) if true_sum > INT64_MAX: print(f"WARNING: Sum {true_sum:.2e} would overflow int64!") return True # Check for sign anomalies (would indicate overflow in fixed-width) if expected_positive and all(x >= 0 for x in arr): if true_sum < 0: # In fixed-width, this means overflow print("WARNING: Positive sum became negative - overflow!") return True return FalseBefore writing any code for a problem, do constraint analysis. Calculate the worst-case value for every sum, product, and intermediate calculation. This 2-minute check prevents hours of debugging overflow issues later.
Integer overflow is a silent killer of algorithmic code. Let's solidify the key principles:
What's Next
The next page covers incorrect base cases—a subtle bug category that corrupts recursion and dynamic programming solutions from their very foundation. Understanding base case design is essential for correct recursive thinking.
You now understand integer overflow: its causes, where it hides in algorithms, and how to prevent it. With constraint analysis as a first step and defensive coding habits, overflow bugs will become rare in your code.