Skip to content

Commit 5d66323

Browse files
fix: high() and low() when number of bits is not a power of 2 (#172)
Functions high() and low() were not clearing the most significant bits of the last limb, leading to values that were outside of the range of the integer. Additionally, for signed integers an an incorrect mask was used to set/clear the most-significant bit. * chore: cleanup function signatures Addresses review comments by @moigagoo
1 parent 470b789 commit 5d66323

File tree

4 files changed

+33
-10
lines changed

4 files changed

+33
-10
lines changed

stint/intops.nim

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,40 @@ import
1717

1818
export StInt
1919

20-
const
21-
signMask = 1.Word shl (WordBitWidth - 1)
22-
clearSignMask = not signMask
23-
2420
# Signedness
2521
# --------------------------------------------------------
2622
{.push raises: [], inline, noinit, gcsafe.}
2723

24+
func signMask(T: typedesc[StInt]): Word {.compileTime.} =
25+
let position = T.bits - (T.bits.wordsRequired-1) * WordBitWidth - 1
26+
Word(1) shl position
27+
28+
func clearSignMask(T: typedesc[StInt]): Word {.compileTime.} =
29+
not signMask(T)
30+
2831
func sign*(a: StInt): int =
2932
## get the sign of `a`
3033
## either -1, 0, or 1
34+
const mask = signMask(typeof(a))
3135
if a.impl.isZero: return 0
32-
if a.limbs[^1] < signMask: 1
36+
if a.limbs[^1] < mask: 1
3337
else: -1
3438

3539
func isNegative*(a: StInt): bool =
36-
a.limbs[^1] >= signMask
40+
const mask = signMask(typeof(a))
41+
a.limbs[^1] >= mask
3742

3843
func isPositive*(a: StInt): bool =
39-
a.limbs[^1] < signMask
44+
const mask = signMask(typeof(a))
45+
a.limbs[^1] < mask
4046

4147
func clearMSB(a: var StInt) =
42-
a.limbs[^1] = a.limbs[^1] and clearSignMask
48+
const mask = clearSignMask(typeof(a))
49+
a.limbs[^1] = a.limbs[^1] and mask
4350

4451
func setMSB(a: var StInt) =
45-
a.limbs[^1] = a.limbs[^1] or signMask
52+
const mask = signMask(typeof(a))
53+
a.limbs[^1] = a.limbs[^1] or mask
4654

4755
func negate*(a: var StInt) =
4856
## two complement negation
@@ -94,6 +102,7 @@ func high*[bits](_: typedesc[StInt[bits]]): StInt[bits] =
94102
# so we only have to unset the most significant bit.
95103
for i in 0 ..< result.limbs.len:
96104
result[i] = high(Word)
105+
result.clearExtraBitsOverMSB()
97106
result.clearMSB
98107

99108
func low*[bits](_: typedesc[StInt[bits]]): StInt[bits] =

stint/private/datatypes.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ template len*(a: StUint): int =
8484

8585
{.push raises: [], inline, noinit, gcsafe.}
8686

87-
template clearExtraBitsOverMSB*(a: var StUint) =
87+
template clearExtraBitsOverMSB*(a: var StUint | StInt) =
8888
## A Stuint is stored in an array of 32 of 64-bit word
8989
## If we do bit manipulation at the word level,
9090
## for example a 8-bit stuint stored in a 64-bit word

stint/uintops.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func one*[bits: static[int]](T: typedesc[StUint[bits]]): T {.inline.} =
4949
func high*[bits](_: typedesc[StUint[bits]]): StUint[bits] {.inline.} =
5050
for i in 0 ..< result.limbs.len:
5151
result[i] = high(Word)
52+
result.clearExtraBitsOverMSB()
5253

5354
func low*[bits](_: typedesc[StUint[bits]]): StUint[bits] {.inline.} =
5455
result.setZero

tests/test_bugfix.nim

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,16 @@ suite "various bugfix":
1818

1919
expect(AssertionDefect):
2020
discard "0bcdef12345".parse(UInt128, 10)
21+
22+
test "high and low when number of bits is not a power of 2":
23+
check StUint[24].high.stuint(128) == 2.u128.pow(24) - 1
24+
check StUint[40].high.stuint(128) == 2.u128.pow(40) - 1
25+
check StUint[96].high.stuint(128) == 2.u128.pow(96) - 1
26+
27+
check StInt[24].high.stint(128) == 2.i128.pow(23) - 1.i128
28+
check StInt[40].high.stint(128) == 2.i128.pow(39) - 1.i128
29+
check StInt[96].high.stint(128) == 2.i128.pow(95) - 1.i128
30+
31+
check StInt[24].low.stint(128) == - 2.i128.pow(23)
32+
check StInt[40].low.stint(128) == - 2.i128.pow(39)
33+
check StInt[96].low.stint(128) == - 2.i128.pow(95)

0 commit comments

Comments
 (0)