Skip to content

Commit

Permalink
add math.isNaN (nim-lang#16179)
Browse files Browse the repository at this point in the history
* add math.isNaN
* isNaN now works with --passc:-ffast-math; tests
* Update lib/pure/math.nim

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
  • Loading branch information
2 people authored and ardek66 committed Mar 26, 2021
1 parent e61ef59 commit 60fbf9c
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 5 deletions.
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
- `strscans.scanf` now supports parsing single characters.


- Added `math.isNaN`.

## Language changes

- `nimscript` now handles `except Exception as e`.
Expand Down
1 change: 1 addition & 0 deletions lib/js/dom.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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.}

Expand Down
21 changes: 21 additions & 0 deletions lib/pure/math.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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: "<math.h>".}
# 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 <https://en.wikipedia.org/wiki/Binomial_coefficient>`_.
runnableExamples:
Expand Down Expand Up @@ -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
Expand Down
31 changes: 26 additions & 5 deletions tests/stdlib/tmath.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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()

0 comments on commit 60fbf9c

Please sign in to comment.