Tighten Int.to bounds and add twos-complement bitcount

master
Marc Tiehuis 2018-07-21 23:33:28 +12:00
parent bbd293355b
commit 07b6a3d335
1 changed files with 91 additions and 12 deletions

View File

@ -115,13 +115,47 @@ pub const Int = struct {
return !r.isOdd();
}
fn bitcount(self: Int) usize {
const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1]));
return usize(@boolToInt(!self.positive)) + u_bit_count;
// Returns the number of bits required to represent the absolute value of self.
fn bitCountAbs(self: Int) usize {
return (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1]));
}
// Returns the number of bits required to represent the integer in twos-complement form.
//
// If the integer is negative the value returned is the number of bits needed by a signed
// integer to represent the value. If positive the value is the number of bits for an
// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
// one greater than the returned value.
//
// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7.
fn bitCountTwosComp(self: Int) usize {
var bits = self.bitCountAbs();
// If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos
// complement requires one less bit.
if (!self.positive) block: {
bits += 1;
if (@popCount(self.limbs[self.len - 1]) == 1) {
for (self.limbs[0 .. self.len - 1]) |limb| {
if (@popCount(limb) != 0) {
break :block;
}
}
bits -= 1;
}
}
return bits;
}
// Returns the approximate size of the integer in the given base. Negative values accomodate for
// the minus sign. This is used for determining the number of characters needed to print the
// value. It is inexact and will exceed the given value by 1-2 digits.
pub fn sizeInBase(self: Int, base: usize) usize {
return (self.bitcount() / math.log2(base)) + 1;
const bit_count = usize(@boolToInt(!self.positive)) + self.bitCountAbs();
return (bit_count / math.log2(base)) + 1;
}
pub fn set(self: *Int, value: var) Allocator.Error!void {
@ -189,9 +223,9 @@ pub const Int = struct {
pub fn to(self: Int, comptime T: type) ConvertError!T {
switch (@typeId(T)) {
TypeId.Int => {
const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T;
const UT = @IntType(false, T.bit_count);
if (self.bitcount() > 8 * @sizeOf(UT)) {
if (self.bitCountTwosComp() > T.bit_count) {
return error.TargetTooSmall;
}
@ -208,9 +242,17 @@ pub const Int = struct {
}
if (!T.is_signed) {
return if (self.positive) r else error.NegativeIntoUnsigned;
return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned;
} else {
return if (self.positive) @intCast(T, r) else -@intCast(T, r);
if (self.positive) {
return @intCast(T, r);
} else {
if (math.cast(T, r)) |ok| {
return -ok;
} else |_| {
return @minValue(T);
}
}
}
},
else => {
@ -1120,24 +1162,61 @@ test "big.int bitcount + sizeInBase" {
var a = try Int.init(al);
try a.set(0b100);
debug.assert(a.bitcount() == 3);
debug.assert(a.bitCountAbs() == 3);
debug.assert(a.sizeInBase(2) >= 3);
debug.assert(a.sizeInBase(10) >= 1);
a.negate();
debug.assert(a.bitCountAbs() == 3);
debug.assert(a.sizeInBase(2) >= 4);
debug.assert(a.sizeInBase(10) >= 2);
try a.set(0xffffffff);
debug.assert(a.bitcount() == 32);
debug.assert(a.bitCountAbs() == 32);
debug.assert(a.sizeInBase(2) >= 32);
debug.assert(a.sizeInBase(10) >= 10);
try a.shiftLeft(a, 5000);
debug.assert(a.bitcount() == 5032);
debug.assert(a.bitCountAbs() == 5032);
debug.assert(a.sizeInBase(2) >= 5032);
a.positive = false;
debug.assert(a.bitcount() == 5033);
debug.assert(a.bitCountAbs() == 5032);
debug.assert(a.sizeInBase(2) >= 5033);
}
test "big.int bitcount/to" {
var a = try Int.init(al);
try a.set(0);
debug.assert(a.bitCountTwosComp() == 0);
// TODO: stack smashing
// debug.assert((try a.to(u0)) == 0);
// TODO: sigsegv
// debug.assert((try a.to(i0)) == 0);
try a.set(-1);
debug.assert(a.bitCountTwosComp() == 1);
debug.assert((try a.to(i1)) == -1);
try a.set(-8);
debug.assert(a.bitCountTwosComp() == 4);
debug.assert((try a.to(i4)) == -8);
try a.set(127);
debug.assert(a.bitCountTwosComp() == 7);
debug.assert((try a.to(u7)) == 127);
try a.set(-128);
debug.assert(a.bitCountTwosComp() == 8);
debug.assert((try a.to(i8)) == -128);
try a.set(-129);
debug.assert(a.bitCountTwosComp() == 9);
debug.assert((try a.to(i9)) == -129);
}
test "big.int string set" {
var a = try Int.init(al);
try a.setString(10, "120317241209124781241290847124");