bigint: itoa works for numbers <= 120 bits.

This commit is contained in:
Jeroen van Rijn
2021-07-18 02:17:51 +02:00
parent e3d8ac559d
commit 341e8a3c99
3 changed files with 60 additions and 19 deletions

View File

@@ -46,13 +46,13 @@ demo :: proc() {
as, bs, cs: string;
err: Error;
a, err = init(4);
a, err = init(-512);
defer destroy(a);
as, err = itoa(a);
fmt.printf("a: %v, err: %v\n\n", as, err);
delete(as);
b, err = init(4);
b, err = init(42);
defer destroy(b);
bs, err = itoa(b);
fmt.printf("b: %s, err: %v\n\n", bs, err);
@@ -68,7 +68,7 @@ demo :: proc() {
err = sub(c, a, b);
fmt.printf("Error: %v\n", err);
as, err = itoa(a);
as, err = itoa(a, 8);
bs, err = itoa(b);
cs, err = itoa(c);
fmt.printf("a: %v, bits: %v\n", as, count_bits(a));

View File

@@ -88,6 +88,7 @@ set_integer :: proc(a: ^Int, n: $T, minimize := false, loc := #caller_location)
a.used = 0;
a.sign = .Zero_or_Positive if n >= 0 else .Negative;
n = abs(n);
for n != 0 {
a.digit[a.used] = DIGIT(n) & _MASK;
a.used += 1;
@@ -96,7 +97,6 @@ set_integer :: proc(a: ^Int, n: $T, minimize := false, loc := #caller_location)
if minimize {
shrink(a);
}
_zero_unused(a);
}

View File

@@ -114,6 +114,9 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> (
Radix defaults to 10.
*/
radix := radix if radix > 0 else 10;
if radix < 2 || radix > 64 {
return 0, .Invalid_Input;
}
/*
Early exit if we were given an empty buffer.
@@ -123,7 +126,7 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> (
return 0, .Buffer_Overflow;
}
/*
Early exit if `Int` == 0 or the entire `Int` fits in a single radix digit.
Fast path for when `Int` == 0 or the entire `Int` fits in a single radix digit.
*/
if is_zero(a) || (a.used == 1 && a.digit[0] < DIGIT(radix)) {
needed := 2 if is_neg(a) else 1;
@@ -148,15 +151,51 @@ itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false) -> (
return written, .OK;
}
/*
Fast path for when `Int` fits within a `_WORD`.
*/
if a.used == 1 || a.used == 2 {
val := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]);
for val > 0 {
if available == 0 {
return written, .Buffer_Overflow;
}
q := val / _WORD(radix);
buffer[written] = RADIX_TABLE[val - (q * _WORD(radix))];
available -= 1;
written += 1;
val = q;
}
if is_neg(a) {
if available == 0 {
return written, .Buffer_Overflow;
}
buffer[written] = '-';
available -= 1;
written += 1;
}
/*
Reverse output.
*/
slice.reverse(buffer[:written]);
if zero_terminate {
if available == 0 {
return written, .Buffer_Overflow;
}
buffer[written] = 0;
written += 1;
}
return written, .OK;
}
/*
Fast path for radixes that are a power of two.
*/
if is_power_of_two(int(radix)) {
return len(buffer), .OK;
// return len(buffer), .OK;
}
return -1, .Unimplemented;
@@ -170,8 +209,6 @@ int_to_cstring :: itoa_cstring;
We size for `string`, not `cstring`.
*/
radix_size :: proc(a: ^Int, radix: i8) -> (size: int, err: Error) {
t := a;
if radix < 2 || radix > 64 {
return -1, .Invalid_Input;
}
@@ -180,22 +217,26 @@ radix_size :: proc(a: ^Int, radix: i8) -> (size: int, err: Error) {
return 1, .OK;
}
t.sign = .Zero_or_Positive;
log: int;
/*
Calculate `log` on a temporary "copy" with its sign set to positive.
*/
t := &Int{
used = a.used,
allocated = a.allocated,
sign = .Zero_or_Positive,
digit = a.digit,
};
log, err = log_n(t, DIGIT(radix));
size, err = log_n(t, DIGIT(radix));
if err != .OK {
return log, err;
return;
}
/*
log truncates to zero, so we need to add one more, and one for `-` if negative.
*/
if is_neg(a) {
return log + 2, .OK;
} else {
return log + 1, .OK;
}
size += 2 if is_neg(a) else 1;
return size, .OK;
}
/*