summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/math.nim45
-rw-r--r--tests/stdlib/tmath.nim33
-rw-r--r--tests/vm/tcastint.nim14
3 files changed, 48 insertions, 44 deletions
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index 577fa47a5..9e48ecf5d 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -175,11 +175,24 @@ when defined(js):
 
   proc toBitsImpl(x: float): array[2, uint32] =
     let buffer = newArrayBuffer(8)
-    let floatBuffer = newFloat64Array(buffer)
-    let uintBuffer = newUint32Array(buffer)
-    floatBuffer[0] = x
-    {.emit: "`result` = `uintBuffer`;".}
-    # result = cast[array[2, uint32]](uintBuffer)
+    let a = newFloat64Array(buffer)
+    let b = newUint32Array(buffer)
+    a[0] = x
+    {.emit: "`result` = `b`;".}
+    # result = cast[array[2, uint32]](b)
+
+  proc jsSetSign(x: float, sgn: bool): float =
+    let buffer = newArrayBuffer(8)
+    let a = newFloat64Array(buffer)
+    let b = newUint32Array(buffer)
+    a[0] = x
+    asm """
+    function updateBit(num, bitPos, bitVal) {
+      return (num & ~(1 << bitPos)) | (bitVal << bitPos);
+    }
+    `b`[1] = updateBit(`b`[1], 31, `sgn`);
+    `result` = `a`[0]
+    """
 
 proc signbit*(x: SomeFloat): bool {.inline, since: (1, 5, 1).} =
   ## Returns true if `x` is negative, false otherwise.
@@ -203,19 +216,21 @@ func copySign*[T: SomeFloat](x, y: T): T {.inline, since: (1, 5, 1).} =
     doAssert copySign(10.0, -1.0) == -10.0
     doAssert copySign(-Inf, -0.0) == -Inf
     doAssert copySign(NaN, 1.0).isNaN
+    doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0
 
   # TODO: use signbit for examples
-  template impl() =
-    if y > 0.0 or (y == 0.0 and 1.0 / y > 0.0):
-      result = abs(x)
-    elif y <= 0.0:
-      result = -abs(x)
-    else: # must be NaN
-      result = abs(x)
-
-  when defined(js): impl()
+  when defined(js):
+    let uintBuffer = toBitsImpl(y)
+    let sgn = (uintBuffer[1] shr 31) != 0
+    result = jsSetSign(x, sgn)
   else:
-    when nimvm: impl()
+    when nimvm: # not exact but we have a vmops for recent enough nim
+      if y > 0.0 or (y == 0.0 and 1.0 / y > 0.0):
+        result = abs(x)
+      elif y <= 0.0:
+        result = -abs(x)
+      else: # must be NaN
+        result = abs(x)
     else: result = c_copysign(x, y)
 
 func classify*(x: float): FloatClass =
diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim
index 894c79369..769d89b64 100644
--- a/tests/stdlib/tmath.nim
+++ b/tests/stdlib/tmath.nim
@@ -197,6 +197,14 @@ template main() =
     doAssert: compiles(5.5 ^ 2.uint8)
     doAssert: not compiles(5.5 ^ 2.2)
 
+  block: # isNaN
+    doAssert NaN.isNaN
+    doAssert not Inf.isNaN
+    doAssert isNaN(Inf - Inf)
+    doAssert not isNaN(0.0)
+    doAssert not isNaN(3.1415926)
+    doAssert not isNaN(0'f32)
+
   block: # signbit
     doAssert not signbit(0.0)
     doAssert signbit(-0.0)
@@ -207,14 +215,6 @@ template main() =
     doAssert signbit(-Inf)
     doAssert not signbit(NaN)
 
-  block: # isNaN
-    doAssert NaN.isNaN
-    doAssert not Inf.isNaN
-    doAssert isNaN(Inf - Inf)
-    doAssert not isNaN(3.1415926)
-    doAssert not isNaN(0'f32)
-
-  block: # signbit
     let x1 = NaN
     let x2 = -NaN
     let x3 = -x1
@@ -269,6 +269,10 @@ template main() =
     doAssert copySign(-NaN, 0.0).isNaN
     doAssert copySign(-NaN, -0.0).isNaN
 
+    doAssert copySign(-1.0, NaN) == 1.0
+    doAssert copySign(-1.0, -NaN) == -1.0
+    doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0
+
   block: # almostEqual
     doAssert almostEqual(3.141592653589793, 3.1415926535897936)
     doAssert almostEqual(1.6777215e7'f32, 1.6777216e7'f32)
@@ -279,7 +283,7 @@ template main() =
     doAssert not almostEqual(Inf, NaN)
     doAssert not almostEqual(NaN, NaN)
 
-  block: # round() tests
+  block: # round
     block: # Round to 0 decimal places
       doAssert round(54.652) == 55.0
       doAssert round(54.352) == 54.0
@@ -300,6 +304,7 @@ template main() =
       doAssert round(2.5) == 3.0
       doAssert round(2.5'f32) == 3.0'f32
       doAssert round(2.5'f64) == 3.0'f64
+
     block: # func round*[T: float32|float64](x: T, places: int): T
       doAssert round(54.345, 0) == 54.0
       template fn(x) =
@@ -311,15 +316,7 @@ template main() =
       fn(54.346)
       fn(54.346'f32)
 
-    when nimvm:
-      discard
-    else:
-      when not defined(js):
-        doAssert copySign(-1.0, -NaN) == -1.0
-        doAssert copySign(10.0, -NaN) == -10.0
-        doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0 # fails in VM
-
-  block:
+  block: # abs
     doAssert 1.0 / abs(-0.0) == Inf
     doAssert 1.0 / abs(0.0) == Inf
     doAssert -1.0 / abs(-0.0) == -Inf
diff --git a/tests/vm/tcastint.nim b/tests/vm/tcastint.nim
index acff0d2b1..a97c81ed2 100644
--- a/tests/vm/tcastint.nim
+++ b/tests/vm/tcastint.nim
@@ -1,7 +1,3 @@
-discard """
-  output: "OK"
-"""
-
 import macros
 
 type
@@ -292,16 +288,12 @@ proc test_float32_castB() =
   # any surprising content.
   doAssert cast[uint64](c) == 3270918144'u64
 
-test()
-test_float_cast()
-test_float32_cast()
-free_integer_casting()
-test_float32_castB()
-static:
+template main() =
   test()
   test_float_cast()
   test_float32_cast()
   free_integer_casting()
   test_float32_castB()
 
-echo "OK"
+static: main()
+main()