F# 中与浮点(双精度类型)的模式匹配

Aph*_*hex 4 floating-point double f# pattern-matching

谁能解释为什么 F# 似乎不喜欢模式处理表达式中的浮点数(Double 类型)?

\n\n
let intDiv x y =\n    match x / y with\n    | 5 -> printfn "Result was five - congrats"; 5\n    | z -> printfn "Result was something else: %i" z; z\n
Run Code Online (Sandbox Code Playgroud)\n\n

匹配int类型:按预期工作。

\n\n
let floatDiv x y =\n    match x / y with\n    | nan      -> printfn "Homie, what did you even do?"; nan\n    | infinity -> printfn "Wow, you got infinity!"; infinity\n    | 5.0      -> printfn "Result was 5 - proud of you"; 5.0\n    | z        -> printfn "Result was something else: %i" z; z\n
Run Code Online (Sandbox Code Playgroud)\n\n

nan这里每次都会匹配匹配大小写,编译器也会警告我有关此\xe2\x80\x94的信息,但是,它似乎也会返回正确的结果。我只期望它与最后一个案例匹配z

\n\n
> floatDiv 10. 3.;;\nHomie, what did you even do?\nval it : float = 3.333333333\n
Run Code Online (Sandbox Code Playgroud)\n

Jus*_*mer 5

正如您所观察到的nan, 和infinity在这里被视为标识符,并且值与它们绑定。

如果检查 F# 规范:https://fsharp.org/specs/language-spec/4.1/FSharpSpec-4.1-latest.pdf

第 7 章(第 115 页)说const表达式是一种模式。

第 4 章(第 36 页)说 aconst包含ieee64.

第 3 章(第 29 页)说 aieee64是 afloat或后面跟着的整数LF

在示例位置我们找到 的定义float

token float =        
   digit+ . digit*         
   digit+ (. digit* )? (e|E) (+|-)? digit+  
Run Code Online (Sandbox Code Playgroud)

该定义仅涵盖 123、3.14、1E99 等情况。它不包括无穷大或 nan。

因此,根据规范,上述行为是预期的。是否应该改变?也许,但涉及更新语言以包含 nan 和无穷大作为 float 常量表达式的一部分。由于ieee确实包含这些值,所以我认为它应该是常量表达式的一部分。

但是,这种更改可能存在风险,因为旧代码中突然nan意味着对方法的引用,而更改后它将是浮点文字。也许有人使用 nan 作为函数名?现在会崩溃,因为这就像给一个函数命名:0

正如@Foole 所提到的,您可以使用活动模式来解决这个问题。

// Define the Active Pattern
let (|Float|Infinity|NaN|) n =
  if System.Double.IsPositiveInfinity   n then Infinity true
  elif System.Double.IsNegativeInfinity n then Infinity false
  elif System.Double.IsNaN              n then NaN
  else Float n

// We can then use the Active Pattern as a "smart" pattern
let floatDiv x y =
  match x / y with
  | NaN          -> printfn "Homie, what did you even do?"; nan
  | Infinity _   -> printfn "Wow, you got infinity!"; infinity
  | Float    5.0 -> printfn "Result was 5 - proud of you"; 5.0
  | Float    z   -> printfn "Result was something else: %f" z; z

let run () =
  floatDiv 1.0 2.0 |> printfn "%A"
  floatDiv 5.0 1.0 |> printfn "%A"
  floatDiv 1.0 0.0 |> printfn "%A"
  floatDiv 0.0 0.0 |> printfn "%A"
Run Code Online (Sandbox Code Playgroud)

将浮点数与特定数字进行比较总是有点“冒险”,因为浮点数本质上通常只是一个近似答案。通常将结果与公差范围进行比较。

此外; nan 让许多开发人员感到困惑,因为大多数涉及 nan 的比较都是错误的。

1.0 < nan // false  <-|
nan < 1.0 // false, <-| these inequalities can break balanced trees algorithms if you use float as a key and happen to insert a nan

nan = nan  // false  <-|
nan <> nan // true   <-| perhaps also surprising?
Run Code Online (Sandbox Code Playgroud)