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:
parent
04b1120334
commit
cde950e1bc
3 changed files with 48 additions and 44 deletions
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue