为什么Python中NaN不触发错误且存在判断、排序等特殊行为?
1 为什么float('nan')没有做单例优化,float('nan') in [NaN]返回False?
首先明确Python列表in运算符的判断逻辑:遍历列表元素时,先判断两个对象身份是否相等(即id(元素) == id(目标)),相等则直接返回True;如果身份不等,再判断值是否相等(元素 == 目标),相等则返回True。
而NaN有一个IEEE 754标准明确规定的核心特性:任何NaN和自身做==比较都会返回False。
至于为什么不把float('nan')做成全局唯一单例,本质原因是IEEE 754标准中NaN本身就不是唯一值:NaN分为信号NaN、静默NaN,还有不同的payload位,仅双精度浮点数的NaN就有2^51-1种不同的二进制表示,不可能统一做成唯一单例。所以每次调用float('nan')都会生成新的浮点数对象,和之前生成的NaN身份不同,同时==比较也返回False,自然就会出现float('nan') in [NaN]返回False的结果。
2 为什么NaN和任何值比较都返回False,而不抛出错误?
这个比较规则完全是Python遵循IEEE 754工业标准的结果,并非Python自行设计。NaN的语义是「非法数值计算的结果」,属于数值计算体系中的通用异常值,C、Java、JavaScript等绝大多数支持浮点数的语言,NaN的比较规则都完全一致。
如果Python自行修改规则,对涉及NaN的比较抛出错误,会导致所有依赖标准浮点数行为的科学计算库(如numpy、scipy)完全无法正常运行,也不符合跨语言的数值计算兼容性要求。
3 为什么包含NaN的序列排序结果不符合预期?
Python的sorted函数默认使用Timsort算法,排序核心逻辑是通过两两比较a < b的结果判定元素顺序。当比较NaN和普通数值时,无论a < NaN还是NaN < a都会返回False,排序算法会判定二者顺序相等,保留二者在原序列中的相对位置。这就导致NaN相当于「固定」在原序列的相对位置上,不会参与普通数值的排序过程,最终得到看起来异常的排序结果。
实用处理建议
- 判断一个值是否为NaN,永远不要使用
x == float('nan'),请使用math.isnan(x)函数 - 对包含NaN的序列排序时,可以通过key参数指定NaN的排序位置,比如要把NaN统一放到序列末尾可以这么写:
import math arr = [3, float('nan'), 4, 2, float('nan'), 1] sorted_arr = sorted(arr, key=lambda x: (math.isnan(x), x)) # 输出结果为 [1, 2, 3, 4, nan, nan]
内容的提问来源于stack exchange,提问作者Ohamma

