diff --git a/changelog.md b/changelog.md index 0e96c868a9f91..ab323337cb606 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 b73a852912dc8..b74a58a1a75b9 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 b2433d30955e8..5f3261f1af512 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -58,6 +58,10 @@ import std/private/since import bitops, fenv +when defined(c) or defined(cpp): + 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 `_. runnableExamples: @@ -133,10 +137,27 @@ 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) + doAssert not isNan(3.1415926) + doAssert not isNan(0'f32) + + template fn: untyped = result = x != x + when nimvm: fn() + else: + when defined(js): fn() + else: result = c_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 diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index ac5ce4c903fdd..18e42cbdcbc03 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()