5.5 Floating-Point Pitfalls: NaN, Infinity, and Comparison Issues
Alright, let’s talk about the part of floating-point that feels like it was designed by someone who’d had a very long day and just started making up rules. We’ve got infinities, we’ve got Not-a-Number, and we’ve got comparisons that will make you question your own sanity. It’s not a bug; it’s a feature. A weird, infuriating, but ultimately logical feature.
The Concept of NaN and Infinity
First, let’s get this straight: Infinity isn’t just a theoretical concept; it’s a literal value you can assign to a variable. This happens when you do something that mathematically explodes towards infinity, like dividing a positive number by zero. And before you yell “that’s undefined!”, remember: we’re not in the realm of pure math anymore. We’re in IEEE 754 land, and here, we have rules.
Similarly, NaN is what you get when an operation genuinely has no meaningful numeric answer. 0.0 / 0.0 is the classic example. It’s not infinite; it’s indeterminate. The key thing to remember about NaN is that it’s poisonous. Almost any operation you do with NaN will result in more NaN. It propagates through your calculations like a gossip in an office, tainting everything it touches.
# Let's see this nonsense in action
positive = 10.0
zero = 0.0
print(positive / zero) # Output: inf (a positive infinity)
print(-positive / zero) # Output: -inf (a negative infinity)
# The truly undefined stuff
print(zero / zero) # Output: nan
print(float('NaN') + 10) # Output: nan. See? Poisonous.
The Maddening Rules of Comparison
This is where your intuition will break down and need a stiff drink. Infinity values behave mostly as you’d expect. They’re bigger than any finite number. It’s NaN that’s the real monster.
Here’s the rule: NaN is not equal to, greater than, or less than anything… including itself. Let that sink in. A NaN is not even equal to itself. This is the single biggest source of floating-point comparison bugs.
x = float('nan')
y = 10.0
z = float('nan')
# Comparisons with anything else
print(x == y) # False
print(x < y) # False
print(x > y) # False
print(x == z) # False. It's not even equal to ANOTHER NaN!
# Checking for infinity is straightforward
inf = float('inf')
print(inf > y) # True
print(inf == inf) # True - because it makes sense!
How to Check for NaN and Infinity Safely
Since you can’t use == to check for NaN, what do you do? You use the math.isnan() and math.isinf() functions. These functions are specifically designed to handle these special values correctly. There’s also math.isfinite(), which returns True for any number that is not either nan or inf—incredibly useful for filtering out these troublemakers from your data.
import math
value = float('nan')
# THE WRONG WAY (don't do this):
if value == float('nan'):
print("You will never see this message. Ever.")
# THE RIGHT WAY:
if math.isnan(value):
print("Found the NaN!")
# Checking for infinity
inf_value = 10.0 / 0.0
if math.isinf(inf_value):
print(f"Yep, that's infinite: {inf_value}")
# Finding safe, finite numbers
data = [1.0, 2.5, float('inf'), float('nan'), 4.2]
clean_data = [x for x in data if math.isfinite(x)]
print(clean_data) # Output: [1.0, 2.5, 4.2]
The Perils of NaNs in Data and Aggregations
This isn’t just an academic problem. If a NaN sneaks into a dataset you’re analyzing, it will silently wreck your results. Most aggregation functions, like sum(), min(), or max(), will return NaN if any element in the series is NaN. It’s the ultimate party pooper.
import numpy as np
data_with_nan = [1.1, 2.2, np.nan, 4.4]
print(sum(data_with_nan)) # Output: nan - useless!
print(min(data_with_nan)) # Output: nan - also useless!
# You have to handle them first, either by filtering or using nan-aware functions
print(np.nansum(data_with_nan)) # Output: 7.7 (1.1+2.2+4.4). This is why we use libraries like NumPy.
The bottom line? Infinity is usually a sign of a potential problem in your logic (like division by zero), but it’s at least predictable. NaN, on the other hand, is a black hole of meaninglessness. You must actively defend against it. Always check your inputs and intermediate calculations if there’s any doubt, and lean on math.isnan() and math.isfinite() like your sanity depends on it—because it does.