decimal: NaN List Membership Depends on Object Identity, Not Value (tested)
Decimal('NaN') never equals another NaN, including itself by value -- that's standard IEEE 754 behavior, inherited by decimal. We measured what that means for Python's in operator, list.count(), and list.index(), which look like they test for value equality but don't always.
1. Two NaN Decimals are never == to each other, as expected
from decimal import Decimal
nan1 = Decimal("NaN")
nan2 = Decimal("NaN")
print(nan1 == nan2)
print(nan1 is nan2)
False
False
Measured: two separately-constructed NaN values are neither == nor the same object -- unsurprising, this is the documented IEEE 754 rule that NaN compares unequal to everything, including another NaN.
2. But "nan1 in [nan1]" is True anyway -- the in operator checks identity first
from decimal import Decimal
nan1 = Decimal("NaN")
nan2 = Decimal("NaN")
print("nan1 in [nan1]:", nan1 in [nan1])
print("nan2 in [nan1]:", nan2 in [nan1])
nan1 in [nan1]: True
nan2 in [nan1]: False
Measured: nan1 in [nan1] is True even though nan1 == nan1 is False -- Python's in operator checks is (object identity) before falling back to ==, and short-circuits to a match on identity alone. A second, value-equal NaN object (nan2) checked against the exact same list is False, because it's neither identical nor equal to what's already there. The result of the exact same-looking membership check silently depends on whether it's literally the same object in memory, not on its value -- the opposite of what in normally means for everything else.
3. count() and index() inherit the same inconsistency
from decimal import Decimal
nan1 = Decimal("NaN")
nan2 = Decimal("NaN")
data = [Decimal("1"), nan1]
print("count(nan1):", data.count(nan1))
print("count(nan2):", data.count(nan2))
print("index(nan1):", data.index(nan1))
try:
data.index(nan2)
except Exception as e:
print("index(nan2) ->", type(e).__name__, e)
count(nan1): 1
count(nan2): 0
index(nan1): 1
index(nan2) -> ValueError Decimal('NaN') is not in list
Measured: list.count() and list.index() use the same identity-then-equality check as in, so they report 1/found for the exact stored NaN object and 0/not-found for an equal-looking one. A "have we already recorded this NaN value" check built on in or count() will only ever say yes for the literal object already in the collection -- reinserting a value that parsed to NaN from a different source (a new Decimal('NaN'), a fresh row from a file) will never match, no matter how many NaNs are already present. If NaN values need deduplication or detection, check with .is_nan() explicitly rather than membership tests.
Frequently asked questions
Why does Decimal('NaN') in a list sometimes return True and sometimes False?
Because Python's in operator checks object identity (is) before falling back to equality (==), and NaN values are never == to anything, including themselves. We measured the exact same NaN object already in a list returning True for in, while a separately-constructed but value-equal NaN returned False against that same list.
Does list.count() work correctly for Decimal NaN values?
Not by value -- we measured count() returning 1 for the exact NaN object stored in the list and 0 for a second, independently-created NaN, even though both print identically as Decimal('NaN'). count() relies on the same identity-or-equality check as the in operator.
How do I check if a list already contains a NaN value?
Use .is_nan() explicitly, e.g. any(x.is_nan() for x in my_list), rather than in or count(). We confirmed those membership tests only match the literal NaN object already present, not any value that happens to also be NaN.
Is this NaN membership behavior specific to Python's decimal module?
No -- it follows from Python's general rule that in checks is before ==, combined with IEEE 754's rule that NaN never equals anything. The same pattern applies to float('nan') in a list. We tested it here specifically against decimal.Decimal('NaN').
Does list.index() raise an error for a NaN that looks like it's in the list?
Yes, measurably -- we confirmed data.index(nan2) raises ValueError even when data contains a different NaN object (nan1) that prints identically, because index() can't find an object that's neither identical nor equal to nan2.
Written with AI assistance. Measured on 2026-10-04 with Python 3.13.5's built-in decimal module: created two separate Decimal('NaN') instances, confirmed they aren't == or is equal to each other, then checked list membership, count(), and index() for each against a list containing only the first instance. PlainHeaders is our extension; Python's decimal module is built in.