回顾以下问题:
NaN
在任何比较中,NaN
总是返回 False
,因此它将保留在列表中的原位置。
>>> sorted([float('nan'), 0])
[nan, 0]
>>> sorted([0, float('nan')])
[0, nan]
-0.0
这与0.0
相等,但具有不同的表示形式、不同的json表示和略微不同的数值属性。它与正零和负零一样存在问题,即正零和负零将保持在原始列表中的相对顺序不变:
>>> sorted([0.0, -0.0])
[0.0, -0.0]
>>> sorted([-0.0, 0.0])
[-0.0, 0.0]
其他解决方案?
@khachik的解决方案在NaN
和-inf
的排序行为上不一致。
>>> key=lambda x: float('-inf') if math.isnan(x) else x
>>> sorted([float('nan'), float('-inf')], key=key)
[nan, -inf]
>>> sorted([float('-inf'), float('nan')], key=key)
[-inf, nan]
解决方案:更复杂的键函数。
因此,存在符号和NaN的问题。我们可以将它们包含在键函数中:
def stable_float_sort_key(x: float):
return math.copysign(1, x), math.isnan(x), x
这适用于上述所有示例:
>>> sorted([float('nan'), 0.0], key=stable_float_sort_key)
[0.0, nan]
>>> sorted([0.0, float('nan')], key=stable_float_sort_key)
[0.0, nan]
>>> sorted([float('nan'), float('-inf')], key=stable_float_sort_key)
[-inf, nan]
>>> sorted([float('-inf'), float('nan')], key=stable_float_sort_key)
[-inf, nan]
>>> sorted([0.0, -0.0], key=stable_float_sort_key)
[-0.0, 0.0]
>>> sorted([-0.0, 0.0], key=stable_float_sort_key)
[-0.0, 0.0]
实际上,您可以编写一个假设检验,以显示它在所有浮点数上都是一致的:
import json
from hypothesis import given, settings
from hypothesis import strategies as st
@given(nums=st.lists(st.floats()), random=st.randoms())
@settings(max_examples=10000)
def test_stable_json_sorting(nums, random):
shuffled = list(nums)
random.shuffle(shuffled)
l1 = sorted(nums, key=stable_float_sort_key)
l2 = sorted(shuffled, key=stable_float_sort_key)
assert json.dumps(l1) == json.dumps(l2)
然而,它确实有一些奇怪之处,因为一些NaN是负数!例如:
>>> sorted([float('nan'), -0.0, 0.0, float('-nan')], key=stable_float_sort_key)
[-0.0, nan, 0.0, nan]
如果这让你感到困扰,你可以通过更改顺序来解决问题:
def stable_float_sort_key(x: float):
return math.isnan(x), math.copysign(1, x), x
这将首先对负数进行排序,然后是正数,最后是NaN。
这些内容有意义吗?
当然,其他回答者正确地指出,在某种意义上,这些都没有意义。比较NaN是某种概念错误。但是,即使在问题“没有意义”的情况下,您可能希望具有不变量,例如将由相同代码生成的浮点数集合序列化为完全相同的JSON表示形式,尽管哈希随机化(我的用例)。那更多是Python代码的形式属性,而不是IEEE标准的“正确答案”。
nan
)。在其他标准中无效吗? - max.sort()
上,直到我已经解决了问题才来到这个问答页面:\感谢您记录下来! - jtlz2