Float Special Values¶
Python floats include special values for infinity and undefined results. Understanding these values is essential for robust numerical code.
Infinity¶
Representing unbounded values.
1. Creating Infinity¶
Multiple ways to create infinite values.
import math
# From string
pos_inf = float('inf')
neg_inf = float('-inf')
print(pos_inf) # inf
print(neg_inf) # -inf
# Using math module
print(math.inf) # inf
print(-math.inf) # -inf
# Check type
print(type(pos_inf)) # <class 'float'>
2. Infinity Comparisons¶
Infinity compares as expected.
import math
inf = float('inf')
# Greater than any finite number
print(inf > 1e308) # True
print(inf > 10**1000) # Error: int too large
# Comparison between infinities
print(float('inf') == float('inf')) # True
print(float('inf') > float('-inf')) # True
# Check for infinity
print(math.isinf(inf)) # True
print(math.isinf(-inf)) # True
print(math.isinf(1e308)) # False
3. Infinity Arithmetic¶
Operations with infinity.
import math
inf = float('inf')
# Arithmetic results
print(inf + 1) # inf
print(inf + inf) # inf
print(inf * 2) # inf
print(inf * -1) # -inf
print(1 / inf) # 0.0
print(-1 / inf) # -0.0
# Undefined operations produce NaN
print(inf - inf) # nan
print(inf / inf) # nan
print(0 * inf) # nan
Not a Number¶
Representing undefined or unrepresentable values.
1. Creating NaN¶
Ways to produce NaN values.
import math
# From string
nan = float('nan')
print(nan) # nan
# Using math module
print(math.nan) # nan
# From undefined operations
print(float('inf') - float('inf')) # nan
print(0.0 / 0.0) # Error! ZeroDivisionError
2. NaN Comparisons¶
NaN has unusual comparison behavior.
import math
nan = float('nan')
# NaN is not equal to anything, including itself
print(nan == nan) # False
print(nan != nan) # True
print(nan < 1) # False
print(nan > 1) # False
print(nan == float('nan')) # False
# Check for NaN
print(math.isnan(nan)) # True
3. NaN Propagation¶
NaN propagates through operations.
import math
nan = float('nan')
# Any operation with NaN produces NaN
print(nan + 1) # nan
print(nan * 0) # nan
print(nan ** 0) # 1.0 (special case!)
print(max(1, 2, nan)) # nan
# Comparison functions return False
print(min(1, nan)) # nan (unexpected behavior)
Checking Special Values¶
Functions for detecting special values.
1. math Functions¶
Built-in checking functions.
import math
values = [1.0, float('inf'), float('-inf'), float('nan')]
for v in values:
print(f"{str(v):5} | "
f"isinf: {math.isinf(v)} | "
f"isnan: {math.isnan(v)} | "
f"isfinite: {math.isfinite(v)}")
# Output:
# 1.0 | isinf: False | isnan: False | isfinite: True
# inf | isinf: True | isnan: False | isfinite: False
# -inf | isinf: True | isnan: False | isfinite: False
# nan | isinf: False | isnan: True | isfinite: False
2. NumPy Functions¶
Array-aware checking.
import numpy as np
arr = np.array([1.0, np.inf, -np.inf, np.nan])
print(np.isinf(arr)) # [False True True False]
print(np.isnan(arr)) # [False False False True]
print(np.isfinite(arr)) # [ True False False False]
# Count special values
print(np.sum(np.isnan(arr))) # 1
print(np.sum(np.isinf(arr))) # 2
3. Filtering Values¶
Remove or replace special values.
import numpy as np
arr = np.array([1.0, 2.0, np.nan, 4.0, np.inf])
# Filter out non-finite values
clean = arr[np.isfinite(arr)]
print(clean) # [1. 2. 4.]
# Replace NaN with a value
filled = np.nan_to_num(arr, nan=0.0, posinf=999.0)
print(filled) # [1. 2. 0. 4. 999.]
Signed Zero¶
Positive and negative zero.
1. Creating Signed Zero¶
Both +0.0 and -0.0 exist.
pos_zero = 0.0
neg_zero = -0.0
print(pos_zero) # 0.0
print(neg_zero) # -0.0
# They are equal
print(pos_zero == neg_zero) # True
2. Distinguishing Zeros¶
Using math.copysign to detect sign.
import math
pos_zero = 0.0
neg_zero = -0.0
# copysign reveals the sign
print(math.copysign(1, pos_zero)) # 1.0
print(math.copysign(1, neg_zero)) # -1.0
# Division behavior differs
print(1 / pos_zero) # inf
print(1 / neg_zero) # -inf
3. When Sign Matters¶
Practical implications of signed zero.
import math
# atan2 distinguishes signed zero
print(math.atan2(0.0, -1)) # π (3.14159...)
print(math.atan2(-0.0, -1)) # -π (-3.14159...)
# Logarithm of zero
# print(math.log(0.0)) # ValueError
# print(math.log(-0.0)) # ValueError
Edge Cases¶
Handling boundary conditions.
1. Overflow to Infinity¶
Large values become infinity.
import sys
# Approaching maximum
large = sys.float_info.max
print(large) # 1.7976931348623157e+308
# Overflow
print(large * 2) # inf
print(1e308 * 10) # inf
2. Underflow to Zero¶
Small values become zero.
import sys
# Approaching minimum
small = sys.float_info.min
print(small) # 2.2250738585072014e-308
# Underflow
print(small / 1e10) # 2.225e-318 (subnormal)
print(small / 1e308) # 0.0 (underflow)
3. Division by Zero¶
Float division by zero produces infinity.
# Float division by zero
print(1.0 / 0.0) # ZeroDivisionError in Python!
# But some operations work
import numpy as np
np.seterr(divide='ignore')
print(np.float64(1.0) / np.float64(0.0)) # inf
# Integer division always raises
# print(1 / 0) # ZeroDivisionError
Defensive Programming¶
Safe handling of special values.
1. Input Validation¶
Check inputs before operations.
import math
def safe_divide(a, b):
"""Division with special value handling."""
if math.isnan(a) or math.isnan(b):
return float('nan')
if b == 0:
if a == 0:
return float('nan')
return math.copysign(float('inf'), a * b)
return a / b
print(safe_divide(1, 2)) # 0.5
print(safe_divide(1, 0)) # inf
print(safe_divide(float('nan'), 1)) # nan
2. Result Validation¶
Check outputs after operations.
import math
def compute_with_check(func, *args):
"""Execute function and validate result."""
result = func(*args)
if math.isnan(result):
raise ValueError("Computation produced NaN")
if math.isinf(result):
raise OverflowError("Computation produced infinity")
return result
# Usage
try:
result = compute_with_check(math.sqrt, -1)
except ValueError as e:
print(f"Error: {e}") # Error: Computation produced NaN
3. Graceful Defaults¶
Provide fallback values.
import math
def safe_log(x, default=float('-inf')):
"""Logarithm with fallback for invalid input."""
if x <= 0 or math.isnan(x):
return default
return math.log(x)
print(safe_log(10)) # 2.302...
print(safe_log(0)) # -inf
print(safe_log(-1)) # -inf
def safe_mean(values, default=0.0):
"""Mean with fallback for empty input."""
finite = [v for v in values if math.isfinite(v)]
if not finite:
return default
return sum(finite) / len(finite)
print(safe_mean([1, 2, float('nan'), 4])) # 2.333...
print(safe_mean([])) # 0.0