From 8bcc343451b045a099ba2b886e956891c2a3cd26 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 28 Nov 2020 18:16:04 -0800 Subject: [PATCH 1/6] add math.isNaN --- changelog.md | 2 ++ lib/js/dom.nim | 1 + lib/pure/math.nim | 9 +++++++++ 3 files changed, 12 insertions(+) diff --git a/changelog.md b/changelog.md index 0e96c868a9f9..ab323337cb60 100644 --- a/changelog.md +++ b/changelog.md @@ -58,6 +58,8 @@ - `strscans.scanf` now supports parsing single characters. +- Added `math.isNaN`. + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/lib/js/dom.nim b/lib/js/dom.nim index b73a852912dc..b74a58a1a75b 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1683,6 +1683,7 @@ proc decodeURIComponent*(uri: cstring): cstring {.importc, nodecl.} proc encodeURIComponent*(uri: cstring): cstring {.importc, nodecl.} proc isFinite*(x: BiggestFloat): bool {.importc, nodecl.} proc isNaN*(x: BiggestFloat): bool {.importc, nodecl.} + ## see also `math.isNaN`. proc newEvent*(name: cstring): Event {.importcpp: "new Event(@)", constructor.} diff --git a/lib/pure/math.nim b/lib/pure/math.nim index b2433d30955e..7f4f81d524e4 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -158,6 +158,15 @@ func classify*(x: float): FloatClass = return fcSubnormal return fcNormal +proc isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} = + ## Returns whether `x` is a `NaN`, more efficiently than via `classify(x) == fcNan`. + ## Does not work with: `--passc:-ffast-math`. + runnableExamples: + doAssert NaN.isNaN + doAssert not Inf.isNaN + doAssert isNaN(Inf - Inf) + result = x != x + func almostEqual*[T: SomeFloat](x, y: T; unitsInLastPlace: Natural = 4): bool {. since: (1, 5), inline.} = ## Checks if two float values are almost equal, using From 2734a378618044e91349d771a9d8f6124f0d6bf5 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 29 Nov 2020 02:03:31 -0800 Subject: [PATCH 2/6] isNaN now works with --passc:-ffast-math; tests --- lib/pure/math.nim | 27 ++++++++++++++++++--------- lib/std/cmath.nim | 6 ++++++ tests/stdlib/tmath.nim | 31 ++++++++++++++++++++++++++----- 3 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 lib/std/cmath.nim diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 7f4f81d524e4..0a90fc227b1f 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -57,6 +57,7 @@ import std/private/since # of the standard library! import bitops, fenv +from std/cmath import nil func binom*(n, k: int): int = ## Computes the `binomial coefficient `_. @@ -133,10 +134,24 @@ type fcInf, ## value is positive infinity fcNegInf ## value is negative infinity +func isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} = + ## Returns whether `x` is a `NaN`, more efficiently than via `classify(x) == fcNan`. + ## Works even with: `--passc:-ffast-math`. + runnableExamples: + doAssert NaN.isNaN + doAssert not Inf.isNaN + doAssert isNaN(Inf - Inf) + template fn: untyped = result = x != x + when nimvm: fn() + else: + when defined(js): fn() + else: result = cmath.isnan(x) + func classify*(x: float): FloatClass = ## Classifies a floating point value. ## ## Returns ``x``'s class as specified by `FloatClass enum<#FloatClass>`_. + ## Doesn't work with: `--passc:-ffast-math`. runnableExamples: doAssert classify(0.3) == fcNormal doAssert classify(0.0) == fcZero @@ -144,6 +159,9 @@ func classify*(x: float): FloatClass = doAssert classify(-0.3/0.0) == fcNegInf doAssert classify(5.0e-324) == fcSubnormal + # xxx support `--passc:-ffast-math`; we could use `isNaN` and make sure + # it works for all floats. + # JavaScript and most C compilers have no classify: if x == 0.0: if 1.0/x == Inf: @@ -158,15 +176,6 @@ func classify*(x: float): FloatClass = return fcSubnormal return fcNormal -proc isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} = - ## Returns whether `x` is a `NaN`, more efficiently than via `classify(x) == fcNan`. - ## Does not work with: `--passc:-ffast-math`. - runnableExamples: - doAssert NaN.isNaN - doAssert not Inf.isNaN - doAssert isNaN(Inf - Inf) - result = x != x - func almostEqual*[T: SomeFloat](x, y: T; unitsInLastPlace: Natural = 4): bool {. since: (1, 5), inline.} = ## Checks if two float values are almost equal, using diff --git a/lib/std/cmath.nim b/lib/std/cmath.nim new file mode 100644 index 000000000000..0faed29533de --- /dev/null +++ b/lib/std/cmath.nim @@ -0,0 +1,6 @@ +##[ +Low level wrappers around C math functions. +]## + +proc isnan*(x: float): bool {.importc: "isnan", header: "".} + # a generic like `x: SomeFloat` might work too if this is implemented via a C macro. diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index ac5ce4c903fd..18e42cbdcbc0 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -12,8 +12,11 @@ discard """ [Suite] ^ ''' +matrix:"; -d:nimTmathCase2 -d:danger --passc:-ffast-math" """ +# xxx: fix bugs for js then add: targets:"c js" + import math, random, os import unittest import sets, tables @@ -37,12 +40,16 @@ suite "random int": var rand: int for i in 1..1000: rand = rand(100..1000) - check rand < 1000 + when defined(js): # xxx bug: otherwise fails + check rand <= 1000 + else: + check rand < 1000 check rand >= 100 test " again gives new numbers": var rand1 = rand(1000000) - os.sleep(200) + when not defined(js): + os.sleep(200) var rand2 = rand(1000000) check rand1 != rand2 @@ -72,7 +79,8 @@ suite "random float": test " again gives new numbers": var rand1:float = rand(1000000.0) - os.sleep(200) + when not defined(js): + os.sleep(200) var rand2:float = rand(1000000.0) check rand1 != rand2 @@ -151,6 +159,7 @@ block: when not defined(js): # Check for no side effect annotation proc mySqrt(num: float): float {.noSideEffect.} = + # xxx unused return sqrt(num) # check gamma function @@ -194,7 +203,8 @@ block: #special case doAssert(classify(trunc(1e1000000)) == fcInf) doAssert(classify(trunc(-1e1000000)) == fcNegInf) - doAssert(classify(trunc(0.0/0.0)) == fcNan) + when not defined(nimTmathCase2): + doAssert(classify(trunc(0.0/0.0)) == fcNan) doAssert(classify(trunc(0.0)) == fcZero) #trick the compiler to produce signed zero @@ -211,7 +221,8 @@ block: doAssert(trunc(0.1'f32) == 0) doAssert(classify(trunc(1e1000000'f32)) == fcInf) doAssert(classify(trunc(-1e1000000'f32)) == fcNegInf) - doAssert(classify(trunc(f_nan.float32)) == fcNan) + when not defined(nimTmathCase2): + doAssert(classify(trunc(f_nan.float32)) == fcNan) doAssert(classify(trunc(0.0'f32)) == fcZero) block: # sgn() tests @@ -272,3 +283,13 @@ block: doAssert log2(2.0'f32) == 1.0'f32 doAssert log2(1.0'f32) == 0.0'f32 doAssert classify(log2(0.0'f32)) == fcNegInf + +template main = + # xxx wrap all under `main` so it also gets tested in vm. + block: # isNaN + doAssert NaN.isNaN + doAssert not Inf.isNaN + doAssert isNaN(Inf - Inf) + +main() +static: main() From d4bd38485ef1d145b8de6525bcbfc18dfa5b5d6f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 3 Dec 2020 09:48:38 -0800 Subject: [PATCH 3/6] address comment --- lib/pure/math.nim | 11 +++++++++-- lib/std/cmath.nim | 6 ------ 2 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 lib/std/cmath.nim diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 0a90fc227b1f..6182d625cebb 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -57,7 +57,14 @@ import std/private/since # of the standard library! import bitops, fenv -from std/cmath import nil + +when defined(c) or defined(cpp): + #[ + Low level wrappers around C math functions. + Consider moving this to a dedicated `std/cmath`. + ]# + proc c_isnan*(x: float): bool {.importc: "isnan", header: "".} + # a generic like `x: SomeFloat` might work too if this is implemented via a C macro. func binom*(n, k: int): int = ## Computes the `binomial coefficient `_. @@ -145,7 +152,7 @@ func isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} = when nimvm: fn() else: when defined(js): fn() - else: result = cmath.isnan(x) + else: result = c_isnan(x) func classify*(x: float): FloatClass = ## Classifies a floating point value. diff --git a/lib/std/cmath.nim b/lib/std/cmath.nim deleted file mode 100644 index 0faed29533de..000000000000 --- a/lib/std/cmath.nim +++ /dev/null @@ -1,6 +0,0 @@ -##[ -Low level wrappers around C math functions. -]## - -proc isnan*(x: float): bool {.importc: "isnan", header: "".} - # a generic like `x: SomeFloat` might work too if this is implemented via a C macro. From 40ca641a0852793c8f4e84d25cd5fb2343eccad9 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 8 Dec 2020 18:27:14 -0800 Subject: [PATCH 4/6] address comment + avoid exporting c_isnan --- lib/pure/math.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 6182d625cebb..cbf8129aa680 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -61,9 +61,10 @@ import bitops, fenv when defined(c) or defined(cpp): #[ Low level wrappers around C math functions. - Consider moving this to a dedicated `std/cmath`. + Consider moving this and other direct c wrappers to a dedicated `std/cmath`, + refs https://github.com/nim-lang/RFCs/issues/92#issuecomment-735328291 ]# - proc c_isnan*(x: float): bool {.importc: "isnan", header: "".} + proc c_isnan(x: float): bool {.importc: "isnan", header: "".} # a generic like `x: SomeFloat` might work too if this is implemented via a C macro. func binom*(n, k: int): int = @@ -148,6 +149,9 @@ func isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} = doAssert NaN.isNaN doAssert not Inf.isNaN doAssert isNaN(Inf - Inf) + doAssert not isNan(3.1415926) + doAssert not isNan(0'f32) + template fn: untyped = result = x != x when nimvm: fn() else: From 6830d1fd58d01bc5dafe8a881079cacc5d9423af Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 11 Dec 2020 10:59:17 +0100 Subject: [PATCH 5/6] Update lib/pure/math.nim --- lib/pure/math.nim | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index cbf8129aa680..c9b8bc5fc698 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -59,11 +59,6 @@ import std/private/since import bitops, fenv when defined(c) or defined(cpp): - #[ - Low level wrappers around C math functions. - Consider moving this and other direct c wrappers to a dedicated `std/cmath`, - refs https://github.com/nim-lang/RFCs/issues/92#issuecomment-735328291 - ]# proc c_isnan(x: float): bool {.importc: "isnan", header: "".} # a generic like `x: SomeFloat` might work too if this is implemented via a C macro. From 5bba1d9777219ef215f13cd2377cf2ff653fe005 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 11 Dec 2020 11:00:34 +0100 Subject: [PATCH 6/6] Update lib/pure/math.nim --- lib/pure/math.nim | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index c9b8bc5fc698..5f3261f1af51 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -165,9 +165,6 @@ func classify*(x: float): FloatClass = doAssert classify(-0.3/0.0) == fcNegInf doAssert classify(5.0e-324) == fcSubnormal - # xxx support `--passc:-ffast-math`; we could use `isNaN` and make sure - # it works for all floats. - # JavaScript and most C compilers have no classify: if x == 0.0: if 1.0/x == Inf: