最小函数中是否有错误?

Sté*_*ent 12 haskell

我有一个mins415920号码列表,全部应该关闭0.1:

Prelude Lib> take 5 mins
[9.83610706554002e-2,9.847021825032128e-2,9.847021825032128e-2,9.898395035483082e-2,9.898395035483082e-2]
Run Code Online (Sandbox Code Playgroud)

然而:

Prelude Lib> minimum mins
0.10087849151477328
Run Code Online (Sandbox Code Playgroud)

minimum函数不应该返回最小值?

这个结果听起来似乎有道理:

Prelude Lib> foldr min 100000 mins
1.1763616593045858e-4
Run Code Online (Sandbox Code Playgroud)

这是一个错误,还是我误解了minimum

类似的问题maximum:

Prelude Lib> maximum mins
3261145.0627630088
Prelude Lib> foldr max 0 mins
0.1207868227600914
Run Code Online (Sandbox Code Playgroud)

对列表进行排序会产生最大值的第三个结果:

Prelude Lib> import Data.List as L
Prelude Lib L> mins' = sort mins
Prelude Lib L> head mins'
1.1763616593045858e-4
Prelude Lib L> last mins'
0.10295664278431726
Run Code Online (Sandbox Code Playgroud)

并且在排序列表上应用minimummaximum产生最小的第三个结果:

Prelude Lib L> minimum mins'
0.10045801977483232
Prelude Lib L> maximum mins'
3261145.0627630088
Run Code Online (Sandbox Code Playgroud)

编辑

在@ max630的评论之后,我用文本编辑器搜索了列表.的3261145.0627630088的确是在这里,而且有一些NaN:

NaN,NaN,3261145.0627630088
Run Code Online (Sandbox Code Playgroud)

结论:它看起来像minimum给出了错误的结果,并maximum给出了正确的结果.

foldr max给出错误的结果,foldl max给出一个好的结果:

> foldl max 0 mins
3261145.0627630088
Run Code Online (Sandbox Code Playgroud)

max*_*630 14

所有问题的原因是列表包含NaN.显然它违反了Ord总排序要求[*],所以你不能指望使用排序的算法 - 选择最小值或排序 - 在输入时产生正确的结果.相反,他们将产生取决于其内部实现的东西,并且可能根据看似不重要的原因(例如输入顺序)而不同.你应该在做其他事情之前将其过滤掉.

[*] Ord没有明确写出法律(EqNaN也没有破坏),但是这里打破整体规则的例子,如维基百科中描述的那样(感谢@sjakobi进行纠正):

Prelude> let nan = 0 / 0
Prelude> nan
NaN
Prelude> nan <= 1
False
Prelude> 1 <= nan
False
Prelude>
Run Code Online (Sandbox Code Playgroud)

  • 需要注意的事项:[Haskell中的NaN-aware min和max](https://hackage.haskell.org/package/ieee754-0.8.0/docs/Numeric-IEEE.html#g:2). (9认同)
  • 将这些问题记录在`Float`的`Eq`和`Ord`实例上会很棒.它们在`ghc-prim`中的`GHC.Classes`中定义. (2认同)