Add math.copySign (#16406)

* add math.copySign
* fix + tests
This commit is contained in:
flywind 2020-12-30 08:09:30 -06:00 • committed by GitHub
commit 515cd45420
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 1 deletions

View file

@ -69,6 +69,7 @@
- `echo` and `debugEcho` will now raise `IOError` if writing to stdout fails. Previous behavior - `echo` and `debugEcho` will now raise `IOError` if writing to stdout fails. Previous behavior
silently ignored errors. See #16366. Use `-d:nimLegacyEchoNoRaise` for previous behavior. silently ignored errors. See #16366. Use `-d:nimLegacyEchoNoRaise` for previous behavior.
- Added `math.copySign`.
- Added new operations for singly- and doubly linked lists: `lists.toSinglyLinkedList` - Added new operations for singly- and doubly linked lists: `lists.toSinglyLinkedList`
and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements
shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes

View file

@ -13,6 +13,9 @@ from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc, arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
floor, ceil, `mod` floor, ceil, `mod`
when declared(math.copySign):
from math import copySign
from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, getAppFilename from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, getAppFilename
from md5 import getMD5 from md5 import getMD5
from sighashes import symBodyDigest from sighashes import symBodyDigest
@ -168,6 +171,9 @@ proc registerAdditionalOps*(c: PCtx) =
wrap1f_math(floor) wrap1f_math(floor)
wrap1f_math(ceil) wrap1f_math(ceil)
when declared(copySign):
wrap2f_math(copySign)
wrap1s(getMD5, md5op) wrap1s(getMD5, md5op)
proc `mod Wrapper`(a: VmArgs) {.nimcall.} = proc `mod Wrapper`(a: VmArgs) {.nimcall.} =

View file

@ -62,6 +62,9 @@ when defined(c) or defined(cpp):
proc c_isnan(x: float): bool {.importc: "isnan", header: "<math.h>".} 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. # a generic like `x: SomeFloat` might work too if this is implemented via a C macro.
proc c_copysign(x, y: cfloat): cfloat {.importc: "copysignf", header: "<math.h>".}
proc c_copysign(x, y: cdouble): cdouble {.importc: "copysign", header: "<math.h>".}
func binom*(n, k: int): int = func binom*(n, k: int): int =
## Computes the `binomial coefficient <https://en.wikipedia.org/wiki/Binomial_coefficient>`_. ## Computes the `binomial coefficient <https://en.wikipedia.org/wiki/Binomial_coefficient>`_.
runnableExamples: runnableExamples:
@ -153,6 +156,40 @@ func isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} =
when defined(js): fn() when defined(js): fn()
else: result = c_isnan(x) else: result = c_isnan(x)
func copySign*[T: SomeFloat](x, y: T): T {.inline, since: (1, 5, 1).} =
## Returns a value with the magnitude of `x` and the sign of `y`;
## this works even if x or y are NaN or zero, both of which can carry a sign.
runnableExamples:
doAssert copySign(1.0, -0.0) == -1.0
doAssert copySign(0.0, -0.0) == -0.0
doAssert copySign(-1.0, 0.0) == 1.0
doAssert copySign(10.0, 0.0) == 10.0
doAssert copySign(Inf, -1.0) == -Inf
doAssert copySign(-Inf, 1.0) == Inf
doAssert copySign(-1.0, NaN) == 1.0
doAssert copySign(10.0, NaN) == 10.0
doAssert copySign(NaN, 0.0).isNaN
doAssert copySign(NaN, -0.0).isNaN
# fails in VM and JS backend
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()
else:
when nimvm: impl()
else: result = c_copysign(x, y)
func classify*(x: float): FloatClass = func classify*(x: float): FloatClass =
## Classifies a floating point value. ## Classifies a floating point value.
## ##
@ -1159,3 +1196,4 @@ func lcm*[T](x: openArray[T]): T {.since: (1, 1).} =
while i < x.len: while i < x.len:
result = lcm(result, x[i]) result = lcm(result, x[i])
inc(i) inc(i)

View file

@ -308,5 +308,53 @@ template main =
doAssert not Inf.isNaN doAssert not Inf.isNaN
doAssert isNaN(Inf - Inf) doAssert isNaN(Inf - Inf)
main() block: # copySign
doAssert copySign(10.0, -1.0) == -10.0
doAssert copySign(-10.0, -1.0) == -10.0
doAssert copySign(-10.0, 1.0) == 10.0
doAssert copySign(float(10), -1.0) == -10.0
doAssert copySign(10.0'f64, -1.0) == -10.0
doAssert copySign(-10.0'f64, -1.0) == -10.0
doAssert copySign(-10.0'f64, 1.0) == 10.0
doAssert copySign(10'f64, -1.0) == -10.0
doAssert copySign(10.0'f32, -1.0) == -10.0
doAssert copySign(-10.0'f32, -1.0) == -10.0
doAssert copySign(-10.0'f32, 1.0) == 10.0
doAssert copySign(10'f32, -1.0) == -10.0
doAssert copySign(Inf, -1.0) == -Inf
doAssert copySign(-Inf, 1.0) == Inf
doAssert copySign(Inf, 1.0) == Inf
doAssert copySign(-Inf, -1.0) == -Inf
doAssert copySign(Inf, 0.0) == Inf
doAssert copySign(Inf, -0.0) == -Inf
doAssert copySign(-Inf, 0.0) == Inf
doAssert copySign(-Inf, -0.0) == -Inf
doAssert copySign(1.0, -0.0) == -1.0
doAssert copySign(0.0, -0.0) == -0.0
doAssert copySign(-1.0, 0.0) == 1.0
doAssert copySign(10.0, 0.0) == 10.0
doAssert copySign(-1.0, NaN) == 1.0
doAssert copySign(10.0, NaN) == 10.0
doAssert copySign(NaN, NaN).isNaN
doAssert copySign(-NaN, NaN).isNaN
doAssert copySign(NaN, -NaN).isNaN
doAssert copySign(-NaN, -NaN).isNaN
doAssert copySign(NaN, 0.0).isNaN
doAssert copySign(NaN, -0.0).isNaN
doAssert copySign(-NaN, 0.0).isNaN
doAssert copySign(-NaN, -0.0).isNaN
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
static: main() static: main()
main()