summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorTimothee Cour <timothee.cour2@gmail.com>2021-02-21 23:47:00 -0800
committerGitHub <noreply@github.com>2021-02-22 08:47:00 +0100
commitcde950e1bc4f8fa3ac9b243cb6117dab1eab3645 (patch)
tree9aa61a808d264a6c197e58c41441dba4b73f55b3 /lib
parent04b11203343b76cb854fcba66cf25d5f7bba3683 (diff)
downloadNim-cde950e1bc4f8fa3ac9b243cb6117dab1eab3645.tar.gz
make copySign for js consistent with other backends (#16609)
* make copySign work more robustly in js
* improve tests
* improve tests/vm/tcastint.nim
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/math.nim45
1 files changed, 30 insertions, 15 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 =