Skip to content

Float Special Values

Python floats include special values for infinity and undefined results. Understanding these values is essential for robust numerical code.

Mental Model

IEEE 754 reserves special bit patterns for three edge cases: positive infinity, negative infinity, and NaN ("not a number"). Infinity behaves like a mathematical limit -- it is greater than every finite number. NaN is the toxic value: any arithmetic with NaN produces NaN, and NaN != NaN is True, making it the only Python value not equal to itself.

Infinity

Representing unbounded values.

1. Creating Infinity

Multiple ways to create infinite values.

```python 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)) # ```

2. Infinity Comparisons

Infinity compares as expected.

```python 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.

```python 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.

```python 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.

```python 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.

```python 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.

```python 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.

```python 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.

```python 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.

```python 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.

```python 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.

```python 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.

```python 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.

```python 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.

```python

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.

```python 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.

```python 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.

```python 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 ```


Exercises

Exercise 1. Write a function classify_float(x) that returns "positive infinity", "negative infinity", "NaN", or "finite" for any given float value. Use the math module.

Solution to Exercise 1

```python import math

def classify_float(x): if math.isnan(x): return "NaN" elif math.isinf(x): return "positive infinity" if x > 0 else "negative infinity" else: return "finite"

print(classify_float(float('inf'))) # positive infinity print(classify_float(float('nan'))) # NaN print(classify_float(3.14)) # finite print(classify_float(float('-inf'))) # negative infinity ```

Check NaN first because math.isinf(nan) returns False, but NaN also fails all comparison operators.


Exercise 2. Demonstrate that float('nan') != float('nan') is True. Then write a function safe_equal(a, b) that correctly handles NaN comparisons (two NaN values should be considered equal).

Solution to Exercise 2

```python import math

NaN is not equal to itself

print(float('nan') != float('nan')) # True

def safe_equal(a, b): if math.isnan(a) and math.isnan(b): return True return a == b

print(safe_equal(float('nan'), float('nan'))) # True print(safe_equal(1.0, 1.0)) # True print(safe_equal(1.0, 2.0)) # False ```

The standard == operator returns False for NaN comparisons. The workaround uses math.isnan() to detect NaN values explicitly.


Exercise 3. Create a list containing [1.0, float('inf'), float('nan'), -2.5, float('-inf')]. Write code that filters out all non-finite values and computes the mean of the remaining finite values.

Solution to Exercise 3

```python import math

values = [1.0, float('inf'), float('nan'), -2.5, float('-inf')]

finite_values = [x for x in values if math.isfinite(x)] mean = sum(finite_values) / len(finite_values)

print(finite_values) # [1.0, -2.5] print(mean) # -0.75 ```

math.isfinite() returns True only for values that are neither infinity nor NaN.