mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-03 03:32:37 +00:00
bigint: itoa works for numbers <= 120 bits.
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user