Как вы заметили, nan
и infinity
рассматриваются здесь как идентификаторы, и значение привязано к ним.
Если проверить спецификацию F #: https://fsharp.org/specs/language-spec/4.1/FSharpSpec-4.1-latest.pdf
Глава 7 (стр. 115) говорит, что выражение const
является шаблоном.
Глава 4 (стр. 36) говорит, что const
включает ieee64
.
Глава 3 (стр. 29) говоритчто ieee64
является либо float
, либо целым числом, за которым следует LF
В месте выборки мы находим определение для float
.
token float =
digit+ . digit*
digit+ (. digit* )? (e|E) (+|-)? digit+
Это определение толькоохватывает случаи как 123, 3.14, 1E99 и так далее.Это не включает бесконечность, ни nan.
Таким образом, согласно спецификации, вышеупомянутое поведение предназначено.Это должно быть изменено?Возможно, но включает обновление языка, чтобы включить nan и бесконечность как часть константных выражений для float.Поскольку ieee
включает эти значения, имеет смысл, я думаю, что оно должно быть частью константного выражения.
Однако, изменение, вероятно, рискованно, поскольку внезапно в старом коде nan
означало ссылку на метод, с изменением это будет литерал с плавающей точкой.Может быть, кто-то использовал nan в качестве имени функции?Теперь это может привести к сбою, потому что это будет похоже на присвоение функции имени: 0
.
Как уже упоминалось @Foole, вы можете решить эту проблему с помощью Active Patterns.
// 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"
Сравнение чисел с плавающей точкойдля определенного числа всегда немного «рискованно», поскольку плавающие числа по своей природе часто являются лишь приблизительным ответом.Обычно сравнивают результат с диапазоном допусков.
Кроме того;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?