为什么不1.0 = 2.0
工作?不是real平等类型?
编号 类型变量''Z
表示操作数为=
必须具有相等类型。
为什么模式中的实数不起作用 [...]?
模式匹配隐式依赖于相等性测试。神秘的错误消息syntax error: inserting EQUALOP
表示 SML/NJ 解析器不允许在需要模式的情况下使用浮点文字,因此程序员无法收到更有意义的类型错误。
详细说明,
From http://www.smlnj.org/doc/FAQ/faq.txt:
问:real 是相等类型吗?
答:SML '90 和 SML/NJ 0.93 中有,但 SML '97 和 SML/NJ 110 中没有。
所以1.0 = 1.0
将导致类型错误,因为“=”要求参数具有相等性
类型。此外,真正的文字不能在模式中使用。
From http://mlton.org/PolymorphicEquality:
无法比较的一种地面类型是真实的。所以,13.0 = 14.0
类型不正确。一个可以用Real.==
比较实数是否相等,但请注意,这与多态相等具有不同的代数性质。
例如,Real.== (0.1 + 0.2, 0.3)
is false
.
From http://sml-family.org/Basis/real.html:
决定 real 是否应该是相等类型,如果是,那么相等意味着什么,也是有问题的。 IEEE 指定在比较中忽略零的符号,并且如果任一参数为 NaN,则相等性计算结果为 false。
这些限制让 SML 程序员感到不安。前者意味着 0 = ~0 为真,而 r/0 = r/~0 为假。后者意味着诸如 r = r 为假这样的异常,或者对于参考单元 rr,我们可以有 rr = rr 但不能有 !rr = !rr。我们接受零的无符号比较,但认为应该保留相等性、结构相等性以及 而不是 o = 的等价性的自反属性。
简而言之:不要使用相等性来比较实数。执行一个ε检验。我建议阅读有关的文章http://floating-point-gui.de/errors/comparison。总之:
不检查实数是否相同,而是检查差异是否非常小。
差值的误差范围 (delta) 与通常称为epsilon.
-
不要将差异与固定值进行比较epsilon:
fun nearlyEqual (a, b, eps) = Real.abs (a-b) < eps
-
不要只是比较relative与差异epsilon:
fun nearlyEqual (a, b, eps) = abs ((a-b)/b) < eps
-
留意边缘情况:
When b = 0.0
它提高了Div
。 (交换a
and b
提供对称的边缘情况。)
When a
and b
位于零的两侧,返回false
即使它们是尽可能小的非零数。
结果不可交换。有些情况下nearlyEqual (a, b, eps)
没有给出相同的结果nearlyEqual (b, a, eps)
.
该指南提供了通用解决方案;翻译成标准机器学习看起来像:
fun nearlyEqual (a, b, eps) =
let val absA = Real.abs a
val absB = Real.abs b
val diff = Real.abs (a - b)
in Real.== (a, b) orelse
( if Real.== (a, 0.0) orelse
Real.== (b, 0.0) orelse
diff < Real.minNormalPos
then diff < eps * Real.minNormalPos
else diff / Real.min (absA + absB, Real.maxFinite) < eps )
end
它还继续警告一些边缘情况:
在某些情况下,上述方法仍然会产生意外结果(特别是,当一个值接近零时比它恰好为零时要严格得多),并且它开发的一些要通过的测试可能指定了不适当的行为对于某些应用程序。在使用它之前,请确保它适合您的应用程序!