diff --git a/src/Fable.Transforms/Replacements.fs b/src/Fable.Transforms/Replacements.fs index b2257a402c..f4876ab97f 100644 --- a/src/Fable.Transforms/Replacements.fs +++ b/src/Fable.Transforms/Replacements.fs @@ -2121,6 +2121,8 @@ let parseNum (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr op Helper.GlobalCall("Number", t, args, memb="isNaN", ?loc=r) |> Some | "IsInfinity", [_] when isFloat -> Helper.LibCall(com, "Double", "isInfinity", t, args, i.SignatureArgTypes, ?loc=r) |> Some + | "IsNormal", [_] when isFloat -> + Helper.LibCall(com, "Double", "isNormal", t, args, i.SignatureArgTypes, ?loc=r) |> Some | ("Parse" | "TryParse") as meth, str::Value(EnumConstant(Value(NumberConstant(style, _),_),_),_)::_ -> let style = int style diff --git a/src/fable-library/Double.ts b/src/fable-library/Double.ts index ac678326ef..cd933f6b08 100644 --- a/src/fable-library/Double.ts +++ b/src/fable-library/Double.ts @@ -1,4 +1,6 @@ import { FSharpRef } from "./Types.js"; +import { doubleToInt64Bits } from './BitConverter.js'; +import * as LongLib from "./lib/long.js"; export function tryParse(str: string, defValue: FSharpRef): boolean { // TODO: test if value is valid and in range @@ -25,3 +27,10 @@ export function parse(str: string): number { export function isInfinity(x: number) { return x === Number.POSITIVE_INFINITY || x === Number.NEGATIVE_INFINITY; } + +// https://github.com/dotnet/runtime/blob/6ff32877f6ae0ddf48218477c7acc78de12f7fbf/src/libraries/System.Private.CoreLib/src/System/Double.cs#L122-L130 +export function isNormal(d: number) { + let bits = doubleToInt64Bits(d); + bits = LongLib.and(bits, 0x7FFFFFFFFFFFFFFF); + return LongLib.lessThan(bits, 0x7FF0000000000000) && (bits != LongLib.ZERO) && (LongLib.and(bits, 0x7FF0000000000000) != LongLib.ZERO); +} \ No newline at end of file diff --git a/tests/Main/ArithmeticTests.fs b/tests/Main/ArithmeticTests.fs index 0dc47d1e12..a373b30821 100644 --- a/tests/Main/ArithmeticTests.fs +++ b/tests/Main/ArithmeticTests.fs @@ -687,4 +687,27 @@ let tests = formatEuro 0.020M |> equal "0,02 €" formatEuro 0.20M |> equal "0,20 €" formatEuro 2.0M |> equal "2,00 €" + + testList "Double.IsNormal works" [ + let testData = [ + // https://github.com/dotnet/runtime/blob/6ff32877f6ae0ddf48218477c7acc78de12f7fbf/src/libraries/System.Runtime/tests/System/DoubleTests.cs#L687-L704 + Double.NegativeInfinity, false + Double.MinValue, true + -2.2250738585072014E-308, true + -2.2250738585072009E-308, false + -4.94065645841247E-324, false + -0.0, false + Double.NaN, false + 0.0, false + 4.94065645841247E-324, false + 2.2250738585072009E-308, false + 2.2250738585072014E-308, true + Double.MaxValue, true + Double.PositiveInfinity, false + ] + + for v, e in testData -> + testCase $"Double.IsNormal({v}) should be {e}" <| fun _ -> + Double.IsNormal v |> equal e + ] ]