make copySign for js consistent with other backends (#16609)

* make copySign work more robustly in js
* improve tests
* improve tests/vm/tcastint.nim
This commit is contained in:
Timothee Cour 2021-02-21 23:47:00 -08:00 • committed by GitHub
commit cde950e1bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 44 deletions

View file

@ -175,11 +175,24 @@ when defined(js):
proc toBitsImpl(x: float): array[2, uint32] = proc toBitsImpl(x: float): array[2, uint32] =
let buffer = newArrayBuffer(8) let buffer = newArrayBuffer(8)
let floatBuffer = newFloat64Array(buffer) let a = newFloat64Array(buffer)
let uintBuffer = newUint32Array(buffer) let b = newUint32Array(buffer)
floatBuffer[0] = x a[0] = x
{.emit: "`result` = `uintBuffer`;".} {.emit: "`result` = `b`;".}
# result = cast[array[2, uint32]](uintBuffer) # 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).} = proc signbit*(x: SomeFloat): bool {.inline, since: (1, 5, 1).} =
## Returns true if `x` is negative, false otherwise. ## 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(10.0, -1.0) == -10.0
doAssert copySign(-Inf, -0.0) == -Inf doAssert copySign(-Inf, -0.0) == -Inf
doAssert copySign(NaN, 1.0).isNaN doAssert copySign(NaN, 1.0).isNaN
doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0
# TODO: use signbit for examples # TODO: use signbit for examples
template impl() = when defined(js):
if y > 0.0 or (y == 0.0 and 1.0 / y > 0.0): let uintBuffer = toBitsImpl(y)
result = abs(x) let sgn = (uintBuffer[1] shr 31) != 0
elif y <= 0.0: result = jsSetSign(x, sgn)
result = -abs(x)
else: # must be NaN
result = abs(x)
when defined(js): impl()
else: 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) else: result = c_copysign(x, y)
func classify*(x: float): FloatClass = func classify*(x: float): FloatClass =

View file

@ -197,6 +197,14 @@ template main() =
doAssert: compiles(5.5 ^ 2.uint8) doAssert: compiles(5.5 ^ 2.uint8)
doAssert: not compiles(5.5 ^ 2.2) 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 block: # signbit
doAssert not signbit(0.0) doAssert not signbit(0.0)
doAssert signbit(-0.0) doAssert signbit(-0.0)
@ -207,14 +215,6 @@ template main() =
doAssert signbit(-Inf) doAssert signbit(-Inf)
doAssert not signbit(NaN) 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 x1 = NaN
let x2 = -NaN let x2 = -NaN
let x3 = -x1 let x3 = -x1
@ -269,6 +269,10 @@ template main() =
doAssert copySign(-NaN, 0.0).isNaN doAssert copySign(-NaN, 0.0).isNaN
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 block: # almostEqual
doAssert almostEqual(3.141592653589793, 3.1415926535897936) doAssert almostEqual(3.141592653589793, 3.1415926535897936)
doAssert almostEqual(1.6777215e7'f32, 1.6777216e7'f32) doAssert almostEqual(1.6777215e7'f32, 1.6777216e7'f32)
@ -279,7 +283,7 @@ template main() =
doAssert not almostEqual(Inf, NaN) doAssert not almostEqual(Inf, NaN)
doAssert not almostEqual(NaN, NaN) doAssert not almostEqual(NaN, NaN)
block: # round() tests block: # round
block: # Round to 0 decimal places block: # Round to 0 decimal places
doAssert round(54.652) == 55.0 doAssert round(54.652) == 55.0
doAssert round(54.352) == 54.0 doAssert round(54.352) == 54.0
@ -300,6 +304,7 @@ template main() =
doAssert round(2.5) == 3.0 doAssert round(2.5) == 3.0
doAssert round(2.5'f32) == 3.0'f32 doAssert round(2.5'f32) == 3.0'f32
doAssert round(2.5'f64) == 3.0'f64 doAssert round(2.5'f64) == 3.0'f64
block: # func round*[T: float32|float64](x: T, places: int): T block: # func round*[T: float32|float64](x: T, places: int): T
doAssert round(54.345, 0) == 54.0 doAssert round(54.345, 0) == 54.0
template fn(x) = template fn(x) =
@ -311,15 +316,7 @@ template main() =
fn(54.346) fn(54.346)
fn(54.346'f32) fn(54.346'f32)
when nimvm: block: # abs
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:
doAssert 1.0 / abs(-0.0) == Inf doAssert 1.0 / abs(-0.0) == Inf
doAssert 1.0 / abs(0.0) == Inf doAssert 1.0 / abs(0.0) == Inf
doAssert -1.0 / abs(-0.0) == -Inf doAssert -1.0 / abs(-0.0) == -Inf

View file

@ -1,7 +1,3 @@
discard """
output: "OK"
"""
import macros import macros
type type
@ -292,16 +288,12 @@ proc test_float32_castB() =
# any surprising content. # any surprising content.
doAssert cast[uint64](c) == 3270918144'u64 doAssert cast[uint64](c) == 3270918144'u64
test() template main() =
test_float_cast()
test_float32_cast()
free_integer_casting()
test_float32_castB()
static:
test() test()
test_float_cast() test_float_cast()
test_float32_cast() test_float32_cast()
free_integer_casting() free_integer_casting()
test_float32_castB() test_float32_castB()
echo "OK" static: main()
main()