diff options
author | Ryan McConnell <rammcconnell@gmail.com> | 2023-06-16 23:42:05 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-17 05:42:05 +0200 |
commit | 647d9611ae81c7af2c86af5b33a14787254896b5 (patch) | |
tree | 199c5d1194e0b97736f7af7665cbf900b57d1db8 | |
parent | e8d0f1c3aedd8290cc972e3d06f642e88dbe783e (diff) | |
download | Nim-647d9611ae81c7af2c86af5b33a14787254896b5.tar.gz |
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>
-rw-r--r-- | compiler/vmhooks.nim | 7 | ||||
-rw-r--r-- | compiler/vmops.nim | 8 | ||||
-rw-r--r-- | lib/pure/math.nim | 38 | ||||
-rw-r--r-- | tests/stdlib/tmath.nim | 10 |
4 files changed, 61 insertions, 2 deletions
diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim index 84ecf586f..1741574b8 100644 --- a/compiler/vmhooks.nim +++ b/compiler/vmhooks.nim @@ -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 diff --git a/compiler/vmops.nim b/compiler/vmops.nim index cf20f1825..8fb1ae901 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -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) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index fc45a6641..7ed84fa71 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -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. diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 2076a6eff..3fcc42ae8 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -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 |