Add divmod (#22102)

* Adding divmod

* Adding support to VM

* Wrapped C structs and funcs

* Fix javascript impl

* Fixing struct compat

* Segregate tests, better compiletime defs

* Using `inline` and switch back to `func`

* Apply suggestions from code review

* Explicit structures

---------

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
This commit is contained in:
Ryan McConnell
2023-06-16 23:42:05 -04:00
committed by GitHub
parent e8d0f1c3ae
commit 647d9611ae
4 changed files with 61 additions and 2 deletions

View File

@@ -39,6 +39,13 @@ proc setResult*(a: VmArgs; v: seq[string]) =
for x in v: n.add newStrNode(nkStrLit, x)
a.slots[a.ra].node = n
proc setResult*(a: VmArgs; v: (BiggestInt, BiggestInt)) =
a.slots[a.ra].ensureKind(rkNode)
var tuplen = newNode(nkTupleConstr)
tuplen.add newIntNode(nkIntLit, v[0])
tuplen.add newIntNode(nkIntLit, v[1])
a.slots[a.ra].node = tuplen
template getReg(a, i): untyped =
doAssert i < a.rc-1
a.slots[i+a.rb+1].unsafeAddr

View File

@@ -12,7 +12,7 @@
from std/math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
floor, ceil, `mod`, cbrt, arcsinh, arccosh, arctanh, erf, erfc, gamma,
lgamma
lgamma, divmod
from std/sequtils import toSeq
when declared(math.copySign):
# pending bug #18762, avoid renaming math
@@ -80,6 +80,11 @@ template wrap2fMath(op) {.dirty.} =
setResult(a, op(getFloat(a, 0), getFloat(a, 1)))
mathop op
template wrap2iMath(op) {.dirty.} =
proc `op Wrapper`(a: VmArgs) {.nimcall.} =
setResult(a, op(getInt(a, 0), getInt(a, 1)))
mathop op
template wrap0(op, modop) {.dirty.} =
proc `op Wrapper`(a: VmArgs) {.nimcall.} =
setResult(a, op())
@@ -224,6 +229,7 @@ proc registerAdditionalOps*(c: PCtx) =
wrap1fMath(erfc)
wrap1fMath(gamma)
wrap1fMath(lgamma)
wrap2iMath(divmod)
when declared(copySign):
wrap2fMath(copySign)

View File

@@ -77,6 +77,36 @@ when defined(c) or defined(cpp):
importc: "frexpf", header: "<math.h>".}
func c_frexp2(x: cdouble, exponent: var cint): cdouble {.
importc: "frexp", header: "<math.h>".}
type
div_t {.importc, header: "<stdlib.h>".} = object
quot: cint
rem: cint
ldiv_t {.importc, header: "<stdlib.h>".} = object
quot: clong
rem: clong
lldiv_t {.importc, header: "<stdlib.h>".} = object
quot: clonglong
rem: clonglong
when cint isnot clong:
func divmod_c(x, y: cint): div_t {.importc: "div", header: "<stdlib.h>".}
when clong isnot clonglong:
func divmod_c(x, y: clonglong): lldiv_t {.importc: "lldiv", header: "<stdlib.h>".}
func divmod_c(x, y: clong): ldiv_t {.importc: "ldiv", header: "<stdlib.h>".}
func divmod*[T: SomeInteger](x, y: T): (T, T) {.inline.} =
## Specialized instructions for computing both division and modulus.
## Return structure is: (quotient, remainder)
runnableExamples:
doAssert divmod(5, 2) == (2, 1)
doAssert divmod(5, -3) == (-1, 2)
when T is cint | clong | clonglong:
let res = divmod_c(x, y)
result[0] = res.quot
result[1] = res.rem
else:
result[0] = x div y
result[1] = x mod y
func binom*(n, k: int): int =
## Computes the [binomial coefficient](https://en.wikipedia.org/wiki/Binomial_coefficient).
@@ -793,6 +823,14 @@ else: # JS
doAssert -6.5 mod 2.5 == -1.5
doAssert 6.5 mod -2.5 == 1.5
doAssert -6.5 mod -2.5 == -1.5
func divmod*(num, denom: int): (int, int) =
runnableExamples:
doAssert divmod(5, 2) == (2, 1)
doAssert divmod(5, -3) == (-1, 2)
result[0] = num div denom
result[1] = num mod denom
func round*[T: float32|float64](x: T, places: int): T =
## Decimal rounding on a binary floating point number.

View File

@@ -186,7 +186,15 @@ template main() =
when not defined(nimTmathCase2):
doAssert classify(trunc(f_nan.float32)) == fcNan
doAssert classify(trunc(0.0'f32)) == fcZero
block: # divmod
doAssert divmod(int.high, 1) == (int.high, 0)
doAssert divmod(-1073741823, 17) == (-63161283, -12)
when not defined(js):
doAssert divmod(int32.high, 1.int32) == (int32.high, 0.int32)
doAssert divmod(1073741823.int32, 5.int32) == (214748364.int32, 3.int32)
doAssert divmod(4611686018427387903.int64, 5.int64) == (922337203685477580.int64, 3.int64)
block: # log
doAssert log(4.0, 3.0) ==~ ln(4.0) / ln(3.0)
doAssert log2(8.0'f64) == 3.0'f64