【问题标题】:Comparing option return value to option value将选项返回值与选项值进行比较
【发布时间】:2018-10-17 20:52:06
【问题描述】:

我正在尝试做一些超级简单的事情......

let Average (a : float list) : (float option) =
    let add (x : float) (y : float) = x+y

    match a.Length with
    | 0 -> None
    | 1 -> None
    | _ -> Some((List.fold add 0.0 a)/(float)a.Length)    

let CompareResult func input expected =
         (func input) = expected

这是我的两个功能。 当我做 比较结果平均值 [5.8;6.6;9.4;3.5;4.0](约 5.86) 我是假的。

任何想法为什么会这样会非常受欢迎。 谢谢

【问题讨论】:

  • 欢迎来到令人兴奋的浮点运算世界。由于微小的差异,两个看似相等的值(都显示为 5.86)可能并不真正相等。见,例如randomascii.wordpress.com/2012/02/25/…
  • 好的,谢谢。我已将代码更改为 ((func input) - expected)
  • 使用System.Double.Epsilonnot recommended 进行相等性测试。

标签: f#


【解决方案1】:

除了内部表示的限制导致的差异之外,该问题还可能是由于在浮点算术中计算平均值时精度损失造成的。

5.86.ToString "G17"
// val it : string = "5,8600000000000003"
(Seq.average [5.8;6.6;9.4;3.5;4.0]).ToString "G17"
// val it : string = "5,8599999999999994"

考虑使用更合适的数值数据类型:

Seq.average [5.8;6.6;9.4;3.5;4.0] = 5.86
// false
Seq.average [5.8M;6.6M;9.4M;3.5M;4.0M] = 5.86M
// true

要回答实际问题,如何对 Option<float> 值进行相等性测试:通过“提升”选项中的浮点数,然后对它们执行相等性测试。 Lift是函数式编程中对monads的常见操作。

非常简单,只有当两个参数都是Some-cases 时,我们才会运行定制测试。这很容易通过对有区别的联合案例进行模式匹配来完成。

let eps = 1e-7  // or whatever is sensible in your domain
let equalityCompareFloatOption x y =
    match x, y with
    | Some x', Some y' -> abs(x' - y') < eps
    | _ -> x = y
// val equalityCompareFloatOption : x:float option -> y:float option -> bool

equalityCompareFloatOption
     (Seq.average[5.8;6.6;9.4;3.5;4.0] |> Some) (Some 5.86)
// val it : bool = true

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多