signed int signedness

This commit is contained in:
jangko 2023-06-13 19:03:43 +07:00
parent 8c5a96463c
commit 76b2baad6f
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
11 changed files with 321 additions and 415 deletions

View File

@ -7,155 +7,204 @@
# #
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import ./private/[datatypes] import
./private/datatypes,
./private/uint_bitwise,
./private/uint_shift,
./private/uint_addsub,
./uintops
export StInt export StInt
#export IntImpl, intImpl, UintImpl, uintImpl, bitsof # TODO: remove the need to export those
#import ./private/initialization const
signMask = 1.Word shl WordBitWidth
clearSignMask = not signMask
func zero*[bits: static[int]](T: typedesc[StInt[bits]]): T {.inline.} = # Signedness
# --------------------------------------------------------
{.push raises: [], inline, noinit, gcsafe.}
func sign*(a: StInt): int =
if a.imp.isZero: return 0
if a.limbs[^1] < signMask: 1
else: -1
func isNegative*(a: StInt): bool =
a.sign < 0
func clearSign(a: var StInt) =
a.limbs[^1] = a.limbs[^1] and clearSignMask
func setSign(a: var StInt) =
a.limbs[^1] = a.limbs[^1] or signMask
func negate*(a: var StInt) =
a.imp.bitnot(a.imp)
a.imp.inc
func neg*(a: StInt): StInt =
result.imp.bitnot(a.imp)
result.imp.inc
func abs*(a: StInt): StInt =
if a.isNegative:
a.neg
else:
a
func `-`*(a: StInt): StInt =
a.neg
{.pop.}
# Initialization
# --------------------------------------------------------
{.push raises: [], inline, noinit, gcsafe.}
func setZero*(a: var StInt) =
## Set ``a`` to 0
a.imp.setZero
func setOne*(a: var StInt) =
a.imp.setOne
func zero*[bits: static[int]](T: typedesc[StInt[bits]]): T =
## Returns the zero of the input type ## Returns the zero of the input type
discard result.setZero
#[ func one*[bits: static[int]](T: typedesc[StInt[bits]]): T =
func one*[bits: static[int]](T: typedesc[StUint[bits]]): T {.inline.} =
## Returns the one of the input type ## Returns the one of the input type
result.data = one(type result.data) result.setOne
import ./private/[int_addsub, uint_addsub] func high*[bits](_: typedesc[StInt[bits]]): StInt[bits] =
# The highest signed int has representation
# 0b0111_1111_1111_1111 ....
# so we only have to unset the most significant bit.
for i in 0 ..< result.limbs.len:
result[i] = high(Word)
result.clearSign
func `+`*(x, y: SomeBigInteger): SomeBigInteger {.inline.} = func low*[bits](_: typedesc[StInt[bits]]): StInt[bits] =
## Integer addition # The lowest signed int has representation
result.data = x.data + y.data # 0b1000_0000_0000_0000 ....
func `+=`*(x: var SomeBigInteger, y: SomeBigInteger) {.inline.} = # so we only have to set the most significant bit.
## Integer addition result.setZero
x.data += y.data result.setSign
func `-`*(x, y: SomeBigInteger): SomeBigInteger {.inline.} =
## Integer substraction
result.data = x.data - y.data
func `-=`*(x: var SomeBigInteger, y: SomeBigInteger) {.inline.} =
## Integer substraction
x.data -= y.data
import ./private/int_negabs {.pop.}
func `-`*(x: StInt): StInt {.inline.} = # Comparisons
## Returns true if input is zero # --------------------------------------------------------
## false otherwise {.push raises: [], inline, noinit, gcsafe.}
result.data = -x.data
func abs*(x: StInt): StInt {.inline.} = func isZero*(a: StInt): bool =
## Returns true if input is zero a.imp.isZero
## false otherwise
result.data = abs(x.data)
import ./private/[int_mul, uint_mul] func `==`*(a, b: StInt): bool =
func `*`*(x, y: SomeBigInteger): SomeBigInteger {.inline.} =
## Integer multiplication
result.data = x.data * y.data
import ./private/[int_div, uint_div]
func `div`*(x, y: SomeBigInteger): SomeBigInteger {.inline.} =
## Integer division
result.data = x.data div y.data
func `mod`*(x, y: SomeBigInteger): SomeBigInteger {.inline.} =
## Integer modulo
## This returns the remainder of x / y.
## i.e. x = y * quotient + remainder
result.data = x.data mod y.data
func divmod*(x, y: SomeBigInteger): tuple[quot, rem: SomeBigInteger] {.inline.} =
## Fused integer division and modulo
## Return both the quotient and remainder
## of x / y
(result.quot.data, result.rem.data) = divmod(x.data, y.data)
import ./private/[int_comparison, uint_comparison]
func `<`*(x, y: SomeBigInteger): bool {.inline.} =
## Unsigned `less than` comparison
x.data < y.data
func `<=`*(x, y: SomeBigInteger): bool {.inline.} =
## Unsigned `less or equal` comparison
x.data <= y.data
func `==`*(x, y: SomeBigInteger): bool {.inline.} =
## Unsigned `equal` comparison ## Unsigned `equal` comparison
x.data == y.data a.imp == b.imp
export `<`, `<=`, `==` # Address Generic Instantiation too nested: https://github.com/status-im/nim-stint/pull/66#issuecomment-427557655
# TODO these exports are needed for the SomeInteger versions - move to stew? func `<`*(a, b: StInt): bool =
export isZero, isOdd, isEven, isNegative ## Unsigned `less than` comparison
let
aSign = a.Sign
bSign = b.Sign
func isZero*(x: SomeBigInteger): bool {.inline.} = if aSign >= 0:
if bSign < 0:
return false
elif bSign >= 0:
return true
a.imp < b.imp
func `<=`*(a, b: StInt): bool =
## Unsigned `less or equal` comparison
not(b < a)
func isOdd*(a: StInt): bool =
## Returns true if input is off
## false otherwise
bool(a[0] and 1)
func isEven*(a: StInt): bool =
## Returns true if input is zero ## Returns true if input is zero
## false otherwise ## false otherwise
x.data.isZero not a.isOdd()
func isNegative*(x: StInt): bool {.inline.} = {.pop.}
## Returns true if input is negative (< 0)
## false otherwise
x.data.isNegative
func isOdd*(x: SomeBigInteger): bool {.inline.} = # Bitwise operations
## Returns true if input is zero # --------------------------------------------------------
## false otherwise {.push raises: [], inline, noinit, gcsafe.}
x.data.isOdd
func isEven*(x: SomeBigInteger): bool {.inline.} = func `not`*(a: StInt): StInt =
## Returns true if input is zero ## Bitwise complement of unsigned integer a
## false otherwise ## i.e. flips all bits of the input
x.data.isEven result.imp.bitnot(a.imp)
export isEven, isOdd func `or`*(a, b: StInt): StInt =
## `Bitwise or` of numbers a and b
result.imp.bitor(a.imp, b.imp)
import ./private/[int_bitwise_ops, uint_bitwise_ops] func `and`*(a, b: StInt): StInt =
## `Bitwise and` of numbers a and b
result.imp.bitand(a.imp, b.imp)
func `not`*(x: SomeBigInteger): SomeBigInteger {.inline.}= func `xor`*(a, b: StInt): StInt =
## Bitwise `not` i.e. flips all bits of the input ## `Bitwise xor` of numbers x and y
result.data = x.data.not result.imp.bitxor(a.imp, b.imp)
func `or`*(x, y: SomeBigInteger): SomeBigInteger {.inline.}=
## Bitwise `or`
result.data = x.data or y.data
func `and`*(x, y: SomeBigInteger): SomeBigInteger {.inline.}=
## Bitwise `and`
result.data = x.data and y.data
func `xor`*(x, y: SomeBigInteger): SomeBigInteger {.inline.}=
## Bitwise `xor`
result.data = x.data xor y.data
func `shr`*(x: SomeBigInteger, y: SomeInteger): SomeBigInteger {.inline.} = {.pop.} # End noInit
result.data = x.data shr y
func `shl`*(x: SomeBigInteger, y: SomeInteger): SomeBigInteger {.inline.} = {.push raises: [], inline, gcsafe.}
result.data = x.data shl y
import ./private/[int_highlow, uint_highlow] func `shr`*(a: StInt, k: SomeInteger): StInt =
## Shift right by k bits, arithmetically
## ~(~a >> k)
var tmp: type a
result.imp.bitnot(a.imp)
tmp.imp.shiftRight(result.imp, k)
result.imp.bitnot(tmp.imp)
func high*[bits](_: typedesc[StInt[bits]]): StInt[bits] {.inline.} = func `shl`*(a: StInt, k: SomeInteger): StInt =
result.data = high(type result.data) ## Shift left by k bits
func high*[bits](_: typedesc[StUint[bits]]): StUint[bits] {.inline.} = result.imp.shiftLeft(a.imp, k)
result.data = high(type result.data)
func low*[bits](_: typedesc[StInt[bits]]): StInt[bits] {.inline.} = {.pop.}
result.data = low(type result.data)
func low*[bits](_: typedesc[StUint[bits]]): StUint[bits] {.inline.} =
result.data = low(type result.data)
import ./private/uint_exp, math # Addsub
# --------------------------------------------------------
{.push raises: [], inline, noinit, gcsafe.}
func pow*(x: StUint, y: Natural): StUint {.inline.} = #[
## Returns x raised at the power of y func `+`*(a, b: StInt): StInt =
when x.data is UintImpl: ## Addition for multi-precision unsigned int
result.data = x.data.pow(y) result.sum(a, b)
else:
result.data = x.data ^ y
func pow*(x: StUint, y: StUint): StUint {.inline.} = func `+=`*(a: var StInt, b: StInt) =
## Returns x raised at the power of y ## In-place addition for multi-precision unsigned int
when x.data is UintImpl: a.sum(a, b)
result.data = x.data.pow(y.data)
else: func `-`*(a, b: StInt): StInt =
result.data = x.data ^ y.data ## Substraction for multi-precision unsigned int
]# result.diff(a, b)
func `-=`*(a: var StInt, b: StInt) =
## In-place substraction for multi-precision unsigned int
a.diff(a, b)
func inc*(a: var StInt, w: Word = 1) =
func `+`*(a: StInt, b: SomeUnsignedInt): StInt =
## Addition for multi-precision unsigned int
## with an unsigned integer
result.sum(a, Word(b))
func `+=`*(a: var StInt, b: SomeUnsignedInt) =
## In-place addition for multi-precision unsigned int
## with an unsigned integer
a.inc(Word(b))
]#
{.pop.}

View File

@ -46,32 +46,24 @@ template static_check_size(T: typedesc[SomeInteger], bits: static[int]) =
func stuint*[T: SomeInteger](n: T, bits: static[int]): StUint[bits] {.inline.}= func stuint*[T: SomeInteger](n: T, bits: static[int]): StUint[bits] {.inline.}=
## Converts an integer to an arbitrary precision integer. ## Converts an integer to an arbitrary precision integer.
when sizeof(n) > sizeof(Word): when sizeof(n) > sizeof(Word):
result.limbs[0] = Word(n and Word.high) result.limbs[0] = Word(n and Word.high)
result.limbs[1] = Word(n shr WordBitWidth) result.limbs[1] = Word(n shr WordBitWidth)
else: else:
result.limbs[0] = Word(n) result.limbs[0] = Word(n)
# func stint*[T: SomeInteger](n: T, bits: static[int]): StInt[bits] {.inline.}= func stint*[T: SomeInteger](n: T, bits: static[int]): StInt[bits] {.inline.}=
# ## Converts an integer to an arbitrary precision signed integer. ## Converts an integer to an arbitrary precision signed integer.
# when T is SomeUnsignedInt:
# when result.data is IntImpl: result.imp = stuint(n, bits)
# static_check_size(T, bits) else:
# when T is SomeSignedInt: if n < 0:
# if n < 0: result.imp = stuint(-n, bits)
# # TODO: when bits >= 128, cannot create from result.negate
# # low(int8-64) else:
# # see: status-im/nim-stint/issues/92 result.imp = stuint(n, bits)
# assignLo(result.data, -n)
# result = -result
# else:
# assignLo(result.data, n)
# else:
# assignLo(result.data, n)
# else:
# result.data = (type result.data)(n)
# func to*(a: SomeInteger, T: typedesc[Stint]): T = func to*(a: SomeInteger, T: typedesc[Stint]): T =
# stint(a, result.bits) stint(a, result.bits)
func to*(a: SomeUnsignedInt, T: typedesc[StUint]): T = func to*(a: SomeUnsignedInt, T: typedesc[StUint]): T =
stuint(a, result.bits) stuint(a, result.bits)
@ -83,9 +75,6 @@ func truncate*(num: StInt or StUint, T: typedesc[SomeInteger]): T {.inline.}=
## For signed result type, result is undefined if input does not fit in the target type. ## For signed result type, result is undefined if input does not fit in the target type.
result = T(num.leastSignificantWord()) result = T(num.leastSignificantWord())
func toInt*(num: StInt or StUint): int {.inline, deprecated:"Use num.truncate(int) instead".}=
num.truncate(int)
func stuint*(a: StUint, bits: static[int]): StUint[bits] {.inline.} = func stuint*(a: StUint, bits: static[int]): StUint[bits] {.inline.} =
## unsigned int to unsigned int conversion ## unsigned int to unsigned int conversion
## smaller to bigger bits conversion will have the same value ## smaller to bigger bits conversion will have the same value
@ -307,46 +296,19 @@ func toString*[bits: static[int]](num: StUint[bits], radix: static[uint8] = 10):
reverse(result) reverse(result)
# func toString*[bits: static[int]](num: Stint[bits], radix: static[int8] = 10): string = func toString*[bits: static[int]](num: StInt[bits], radix: static[int8] = 10): string =
# ## Convert a Stint or StUint to string. ## Convert a Stint or StUint to string.
# ## In case of negative numbers: ## In case of negative numbers:
# ## - they are prefixed with "-" for base 10. ## - they are prefixed with "-" for base 10.
# ## - if not base 10, they are returned raw in two-complement form. ## - if not base 10, they are returned raw in two-complement form.
let isNeg = num.isNegative
if radix == 10 and isNeg:
"-" & toString(num.neg.imp, radix)
else:
toString(num.imp, radix)
# static: doAssert (radix >= 2) and radix <= 16, "Only base from 2..16 are supported" func `$`*(num: StInt or StUint): string {.inline.}=
# # TODO: use static[range[2 .. 16]], not supported at the moment (2018-04-26) toString(num, 10)
# const hexChars = "0123456789abcdef"
# const base = radix.int8.StUint(bits)
# result = ""
# type T = StUint[bits]
# let isNeg = num.isNegative
# let num = convert[T](if radix == 10 and isNeg: -num
# else: num)
# var (q, r) = divmod(num, base)
# while true:
# when bitsof(r.data) <= 64:
# result.add hexChars[r.data.int]
# else:
# result.add hexChars[r.truncate(int)]
# if q.isZero:
# break
# (q, r) = divmod(q, base)
# if isNeg and radix == 10:
# result.add '-'
# reverse(result)
# func `$`*(num: Stint or StUint): string {.inline.}=
# when num.data is SomeInteger:
# $num.data
# else:
# toString(num, 10)
func toHex*[bits: static[int]](num: StInt[bits] or StUint[bits]): string {.inline.}= func toHex*[bits: static[int]](num: StInt[bits] or StUint[bits]): string {.inline.}=
## Convert to a hex string. ## Convert to a hex string.

View File

@ -40,7 +40,7 @@ type
StInt*[bits: static[int]] = object StInt*[bits: static[int]] = object
## Stack-based integer ## Stack-based integer
## Signed ## Signed
limbs*: array[bits.wordsRequired, Word] imp*: StUint[bits]
# {.borrow: `.`.} only works with nim-devel # {.borrow: `.`.} only works with nim-devel
# StInt*[bits: static[int]] {.borrow: `.`.} = distinct StUint[bits] # StInt*[bits: static[int]] {.borrow: `.`.} = distinct StUint[bits]
@ -94,6 +94,10 @@ func usedBitsAndWords*(a: openArray[Word]): tuple[bits, words: int] =
# Accessors # Accessors
# -------------------------------------------------------- # --------------------------------------------------------
template limbs*(a: StInt): untyped =
# TODO: remove this when we switch to borrow `.`
a.imp.limbs
template `[]`*(a: SomeBigInteger, i: SomeInteger or BackwardsIndex): Word = template `[]`*(a: SomeBigInteger, i: SomeInteger or BackwardsIndex): Word =
a.limbs[i] a.limbs[i]

View File

@ -1,49 +0,0 @@
# Stint
# Copyright 2018-2023 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
./datatypes, ./conversion, ./int_comparison,
./uint_addsub, ./uint_comparison
func `+`*(x, y: IntImpl): IntImpl {.inline.}=
# Addition for multi-precision signed int.
type SubTy = type x.hi
result.lo = x.lo + y.lo
result.hi = (result.lo < y.lo).toSubtype(SubTy) + x.hi + y.hi
when compileOption("boundChecks"):
if unlikely(
not(result.isNegative xor x.isNegative) or
not(result.isNegative xor y.isNegative)
):
return
raise newException(OverflowDefect, "Addition overflow")
func `+=`*(x: var IntImpl, y: IntImpl) {.inline.}=
## In-place addition for multi-precision signed int.
x = x + y
func `-`*(x, y: IntImpl): IntImpl {.inline.}=
# Substraction for multi-precision signed int.
type SubTy = type x.hi
result.lo = x.lo - y.lo
result.hi = x.hi - y.hi - (x.lo < y.lo).toSubtype(SubTy)
when compileOption("boundChecks"):
if unlikely(
not(result.isNegative xor x.isNegative) or
not(result.isNegative xor y.isNegative.not)
):
return
raise newException(OverflowDefect, "Substraction underflow")
func `-=`*(x: var IntImpl, y: IntImpl) {.inline.}=
## In-place substraction for multi-precision signed int.
x = x - y

View File

@ -1,72 +0,0 @@
# Stint
# Copyright 2018-2023 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ./datatypes, ./bitops2_priv, ./uint_bitwise_ops, ./compiletime_cast
func `not`*(x: IntImpl): IntImpl {.inline.}=
## Bitwise complement of unsigned integer x
applyHiLo(x, `not`)
func `or`*(x, y: IntImpl): IntImpl {.inline.}=
## `Bitwise or` of numbers x and y
applyHiLo(x, y, `or`)
func `and`*(x, y: IntImpl): IntImpl {.inline.}=
## `Bitwise and` of numbers x and y
applyHiLo(x, y, `and`)
func `xor`*(x, y: IntImpl): IntImpl {.inline.}=
## `Bitwise xor` of numbers x and y
applyHiLo(x, y, `xor`)
func `shl`*(x: IntImpl, y: SomeInteger): IntImpl {.inline.}=
## Compute the `shift left` operation of x and y
# Note: inlining this poses codegen/aliasing issue when doing `x = x shl 1`
# TODO: would it be better to reimplement this with words iteration?
const halfSize: type(y) = bitsof(x) div 2
type HiType = type(result.hi)
if y == 0:
return x
elif y == halfSize:
result.hi = convert[HiType](x.lo)
elif y < halfSize:
# `shr` in this equation uses uint version
result.hi = (x.hi shl y) or convert[HiType](x.lo shr (halfSize - y))
result.lo = x.lo shl y
else:
result.hi = convert[HiType](x.lo shl (y - halfSize))
template createShr(name, operator: untyped) =
template name(x, y: SomeInteger): auto =
operator(x, y)
func name*(x: IntImpl, y: SomeInteger): IntImpl {.inline.}=
## Compute the `arithmetic shift right` operation of x and y
## Similar to C standard, result is undefined if y is bigger
## than the number of bits in x.
const halfSize: type(y) = bitsof(x) div 2
type LoType = type(result.lo)
if y == 0:
return x
elif y == halfSize:
result.lo = convert[LoType](x.hi)
result.hi = name(x.hi, halfSize-1)
elif y < halfSize:
result.lo = (x.lo shr y) or convert[LoType](x.hi shl (halfSize - y))
result.hi = name(x.hi, y)
else:
result.lo = convert[LoType](name(x.hi, (y - halfSize)))
result.hi = name(x.hi, halfSize-1)
createShr(shrOfShr, `shr`)
template `shr`*(a, b: typed): untyped =
shrOfShr(a, b)

View File

@ -1,49 +0,0 @@
# Stint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ./datatypes, ./uint_comparison
func isZero*(n: SomeSignedInt): bool {.inline.} =
n == 0
func isZero*(n: IntImpl): bool {.inline.} =
n.hi.isZero and n.lo.isZero
func isNegative*(n: SomeSignedInt): bool {.inline.} =
n < 0
func isNegative*(n: IntImpl): bool {.inline.} =
## Returns true if a number is negative:
n.hi.isNegative
func `<`*(x, y: IntImpl): bool {.inline.}=
# Lower comparison for multi-precision integers
x.hi < y.hi or
(x.hi == y.hi and x.lo < y.lo)
func `==`*(x, y: IntImpl): bool {.inline.}=
# Equal comparison for multi-precision integers
x.hi == y.hi and x.lo == y.lo
func `<=`*(x, y: IntImpl): bool {.inline.}=
# Lower or equal comparison for multi-precision integers
x.hi < y.hi or
(x.hi == y.hi and x.lo <= y.lo)
func isOdd*(x: SomeSignedInt): bool {.inline.}=
bool(x and 1)
func isEven*(x: SomeSignedInt): bool {.inline.}=
not x.isOdd
func isEven*(x: IntImpl): bool {.inline.}=
x.lo.isEven
func isOdd*(x: IntImpl): bool {.inline.}=
not x.isEven

View File

@ -1,27 +0,0 @@
# Stint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ./datatypes, ./int_bitwise_ops, ./initialization, ./uint_highlow, typetraits
# XXX There's some Araq reason why this isn't part of the std lib..
func high(T: typedesc[SomeUnsignedInt]): T =
not T(0)
func high*[T, T2](_: typedesc[IntImpl[T, T2]]): IntImpl[T, T2] {.inline.}=
# The highest signed int has representation
# 0b0111_1111_1111_1111 ....
# so we only have to unset the most significant bit.
result.hi = high(type result.hi)
result.lo = high(type result.lo)
func low*[T, T2](_: typedesc[IntImpl[T, T2]]): IntImpl[T, T2] {.inline.}=
# The lowest signed int has representation
# 0b1000_0000_0000_0000 ....
# so we only have to set the most significant bit.
not high(IntImpl[T, T2])

View File

@ -1,29 +0,0 @@
# Stint
# Copyright 2018 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
./datatypes,
./initialization, ./int_highlow,
./int_addsub, ./int_comparison, ./int_bitwise_ops
func `-`*(x: IntImpl): IntImpl {.inline.}=
# Negate a multi-precision signed int.
when compileOption("boundChecks"):
if unlikely(x == low(type x)):
raise newException(OverflowError, "The lowest negative number cannot be negated")
result = not x
result += one(type x)
func abs*[T: IntImpl](x: T): T {.inline.}=
## Returns the absolute value of a signed int.
result = if x.isNegative: -x
else: x

View File

@ -164,7 +164,7 @@ func divRem*(
let rLen = bLen let rLen = bLen
if unlikely(bBits == 0): if unlikely(bBits == 0):
raise newException(DivByZeroError, "You attempted to divide by zero") raise newException(DivByZeroDefect, "You attempted to divide by zero")
if aBits < bBits: if aBits < bBits:
# if a uses less bits than b, # if a uses less bits than b,

View File

@ -5,5 +5,6 @@ template ctCheck*(cond: untyped) =
doAssert(cond) doAssert(cond)
template ctTest*(name: string, body: untyped) = template ctTest*(name: string, body: untyped) =
body block:
echo "[OK] compile time ", name body
echo "[OK] compile time ", name

View File

@ -0,0 +1,116 @@
# Stint
# Copyright 2018-2023 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import ../stint, unittest, test_helpers
#[
template chkAddition(chk, a, b, c, bits: untyped) =
block:
let x = stuint(a, bits)
let y = stuint(b, bits)
chk x + y == stuint(c, bits)
]#
template testSignedness(chk, tst: untyped) =
tst "positive sign":
let a = stint(1, 128)
chk a.sign > 0
chk a.isNegative == false
let b = stint(uint32.high, 128)
chk b.sign > 0
chk b.isNegative == false
let c = stint(uint64.high, 128)
chk c.sign > 0
chk c.isNegative == false
let aa = stint(1, 256)
chk aa.sign > 0
chk aa.isNegative == false
let bb = stint(uint32.high, 256)
chk bb.sign > 0
chk bb.isNegative == false
let cc = stint(uint64.high, 256)
chk cc.sign > 0
chk cc.isNegative == false
var zz: StInt[128]
zz.setOne
chk zz.sign > 0
chk zz.isNegative == false
let yy = StInt[128].one
chk yy.sign > 0
chk yy.isNegative == false
tst "zero sign":
let a = stint(0, 128)
chk a.sign == 0
chk a.isNegative == false
let aa = stint(0, 256)
chk aa.sign == 0
chk aa.isNegative == false
var zz: StInt[128]
zz.setZero
chk zz.sign == 0
chk zz.isNegative == false
let yy = StInt[128].zero
chk yy.sign == 0
chk yy.isNegative == false
tst "negative sign":
let a = stint(-1, 128)
chk a.sign < 0
chk a.isNegative == true
let aa = stint(-1, 256)
chk aa.sign < 0
chk aa.isNegative == true
let zz = -1.i128
chk zz.sign < 0
chk zz.isNegative == true
let yy = -1.i256
chk yy.sign < 0
chk yy.isNegative == true
tst "abs":
let a = -1.i128
let aa = a.abs
chk aa == 1.i128
let b = -1.i256
let bb = b.abs
chk bb == 1.i256
tst "negate":
var a = -1.i128
a.negate
chk a == 1.i128
var b = -1.i256
b.negate
chk b == 1.i256
let c = -1.i256
let d = -c
chk d == 1.i256
static:
testSignedness(ctCheck, ctTest)
suite "Signed integer signedness":
testSignedness(check, test)