Support 128-bit integers i128 u128

This commit is contained in:
gingerBill
2019-05-28 20:27:45 +01:00
parent 2b080dbbc2
commit 3d2279fba0
10 changed files with 582 additions and 51 deletions
+99
View File
@@ -495,6 +495,71 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
_pad(fi, s);
}
_fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: int, digits: string) {
_, neg := strconv.is_integer_negative_128(u, is_signed, bit_size);
BUF_SIZE :: 256;
if fi.width_set || fi.prec_set {
width := fi.width + fi.prec + 3; // 3 extra bytes for sign and prefix
if width > BUF_SIZE {
// TODO(bill):????
panic("_fmt_int: buffer overrun. Width and precision too big");
}
}
prec := 0;
if fi.prec_set {
prec = fi.prec;
if prec == 0 && u == 0 {
prev_zero := fi.zero;
fi.zero = false;
fmt_write_padding(fi, fi.width);
fi.zero = prev_zero;
return;
}
} else if fi.zero && fi.width_set {
prec = fi.width;
if neg || fi.plus || fi.space {
// There needs to be space for the "sign"
prec -= 1;
}
}
switch base {
case 2, 8, 10, 12, 16:
break;
case:
panic("_fmt_int: unknown base, whoops");
}
buf: [256]byte;
start := 0;
flags: strconv.Int_Flags;
if fi.hash && !fi.zero do flags |= {.Prefix};
if fi.plus do flags |= {.Plus};
if fi.space do flags |= {.Space};
s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags);
if fi.hash && fi.zero {
c: byte = 0;
switch base {
case 2: c = 'b';
case 8: c = 'o';
case 12: c = 'z';
case 16: c = 'x';
}
if c != 0 {
strings.write_byte(fi.buf, '0');
strings.write_byte(fi.buf, c);
}
}
prev_zero := fi.zero;
defer fi.zero = prev_zero;
fi.zero = false;
_pad(fi, s);
}
__DIGITS_LOWER := "0123456789abcdefx";
__DIGITS_UPPER := "0123456789ABCDEFX";
@@ -533,6 +598,31 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) {
}
}
fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: rune) {
switch verb {
case 'v': _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER);
case 'b': _fmt_int_128(fi, u, 2, is_signed, bit_size, __DIGITS_LOWER);
case 'o': _fmt_int_128(fi, u, 8, is_signed, bit_size, __DIGITS_LOWER);
case 'd': _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER);
case 'z': _fmt_int_128(fi, u, 12, is_signed, bit_size, __DIGITS_LOWER);
case 'x': _fmt_int_128(fi, u, 16, is_signed, bit_size, __DIGITS_LOWER);
case 'X': _fmt_int_128(fi, u, 16, is_signed, bit_size, __DIGITS_UPPER);
case 'c', 'r':
fmt_rune(fi, rune(u), verb);
case 'U':
r := rune(u);
if r < 0 || r > utf8.MAX_RUNE {
fmt_bad_verb(fi, verb);
} else {
strings.write_string(fi.buf, "U+");
_fmt_int_128(fi, u, 16, false, bit_size, __DIGITS_UPPER);
}
case:
fmt_bad_verb(fi, verb);
}
}
_pad :: proc(fi: ^Info, s: string) {
if !fi.width_set {
strings.write_string(fi.buf, s);
@@ -1359,6 +1449,15 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
case i64be: fmt_int(fi, u64(a), true, 64, verb);
case u64be: fmt_int(fi, u64(a), false, 64, verb);
case i128: fmt_int_128(fi, u128(a), true, 128, verb);
case u128: fmt_int_128(fi, u128(a), false, 128, verb);
case i128le: fmt_int_128(fi, u128(a), true, 128, verb);
case u128le: fmt_int_128(fi, u128(a), false, 128, verb);
case i128be: fmt_int_128(fi, u128(a), true, 128, verb);
case u128be: fmt_int_128(fi, u128(a), false, 128, verb);
case: fmt_value(fi, arg, verb);
}
+199
View File
@@ -509,3 +509,202 @@ quo_complex128 :: proc(n, m: complex128) -> complex128 {
return complex(e, f);
}
foreign {
@(link_name="llvm.cttz.i8") _ctz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
@(link_name="llvm.cttz.i16") _ctz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
@(link_name="llvm.cttz.i32") _ctz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
@(link_name="llvm.cttz.i64") _ctz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
}
_ctz :: proc{
_ctz_u8,
_ctz_u16,
_ctz_u32,
_ctz_u64,
};
foreign {
@(link_name="llvm.ctlz.i8") _clz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
@(link_name="llvm.ctlz.i16") _clz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
@(link_name="llvm.ctlz.i32") _clz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
@(link_name="llvm.ctlz.i64") _clz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
}
_clz :: proc{
_clz_u8,
_clz_u16,
_clz_u32,
_clz_u64,
};
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
n := transmute([2]u64)a;
d := transmute([2]u64)b;
q, r: [2]u64 = ---, ---;
sr: u32 = 0;
low :: ODIN_ENDIAN == "big" ? 1 : 0;
high :: 1 - low;
U64_BITS :: 8*size_of(u64);
U128_BITS :: 8*size_of(u128);
// Special Cases
if n[high] == 0 {
if d[high] == 0 {
if rem != nil {
rem^ = u128(n[low] % d[low]);
}
return u128(n[low] / d[low]);
}
if rem != nil {
rem^ = u128(n[low]);
}
return 0;
}
if d[low] == 0 {
if d[high] == 0 {
if rem != nil {
rem^ = u128(n[high] % d[low]);
}
return u128(n[high] / d[low]);
}
if n[low] == 0 {
if rem != nil {
r[high] = n[high] % d[high];
r[low] = 0;
rem^ = transmute(u128)r;
}
return u128(n[high] / d[high]);
}
if d[high] & (d[high]-1) == 0 {
if rem != nil {
r[low] = n[low];
r[high] = n[high] & (d[high] - 1);
rem^ = transmute(u128)r;
}
return u128(n[high] >> _ctz(d[high]));
}
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])));
if sr > U64_BITS - 2 {
if rem != nil {
rem^ = a;
}
return 0;
}
sr += 1;
q[low] = 0;
q[high] = n[low] << u64(U64_BITS - sr);
r[high] = n[high] >> sr;
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
} else {
if d[high] == 0 {
if d[low] & (d[low] - 1) == 0 {
if rem != nil {
rem^ = u128(n[low] & (d[low] - 1));
}
if d[low] == 1 {
return a;
}
sr = u32(_ctz(d[low]));
q[high] = n[high] >> sr;
q[low] = (n[high] << (U64_BITS-sr)) | (n[low] >> sr);
return transmute(u128)q;
}
sr = 1 + U64_BITS + u32(_clz(d[low])) - u32(_clz(n[high]));
switch {
case sr == U64_BITS:
q[low] = 0;
q[high] = n[low];
r[high] = 0;
r[low] = n[high];
case sr < U64_BITS:
q[low] = 0;
q[high] = n[low] << (U64_BITS - sr);
r[high] = n[high] >> sr;
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
case:
q[low] = n[low] << (U128_BITS - sr);
q[high] = (n[high] << (U128_BITS - sr)) | (n[low] >> (sr - U64_BITS));
r[high] = 0;
r[low] = n[high] >> (sr - U64_BITS);
}
} else {
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])));
if sr > U64_BITS - 1 {
if rem != nil {
rem^ = a;
}
return 0;
}
sr += 1;
q[low] = 0;
if sr == U64_BITS {
q[high] = n[low];
r[high] = 0;
r[low] = n[high];
} else {
r[high] = n[high] >> sr;
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
q[high] = n[low] << (U64_BITS - sr);
}
}
}
carry: u32 = 0;
r_all: u128 = ---;
for ; sr > 0; sr -= 1 {
r[high] = (r[high] << 1) | (r[low] >> (U64_BITS - 1));
r[low] = (r[low] << 1) | (q[high] >> (U64_BITS - 1));
q[high] = (q[high] << 1) | (q[low] >> (U64_BITS - 1));
q[low] = (q[low] << 1) | u64(carry);
r_all = transmute(u128)r;
s := i128(b - r_all - 1) >> (U128_BITS - 1);
carry = u32(s & 1);
r_all -= b & transmute(u128)s;
r = transmute([2]u64)r_all;
}
q_all := ((transmute(u128)q) << 1) | u128(carry);
if rem != nil {
rem^ = r_all;
}
return q_all;
}
@(link_name="__umodti3")
umodti3 :: proc "c" (a, b: i128) -> i128 {
s_a := a >> (128 - 1);
s_b := b >> (128 - 1);
an := (a ~ s_a) - s_a;
bn := (b ~ s_b) - s_b;
r: u128 = ---;
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
return (transmute(i128)r ~ s_a) - s_a;
}
@(link_name="__udivmodti4")
udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
return udivmod128(a, b, rem);
}
@(link_name="__udivti3")
udivti3 :: proc "c" (a, b: u128) -> u128 {
return udivmodti4(a, b, nil);
}
+76
View File
@@ -359,3 +359,79 @@ append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: i
copy(buf, out);
return string(buf[0:len(out)]);
}
is_integer_negative_128 :: proc(u: u128, is_signed: bool, bit_size: int) -> (unsigned: u128, neg: bool) {
if is_signed {
switch bit_size {
case 8:
i := i8(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 16:
i := i16(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 32:
i := i32(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 64:
i := i64(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 128:
i := i128(u);
neg = i < 0;
u = u128(abs(i128(i)));
case:
panic("is_integer_negative: Unknown integer size");
}
}
return u, neg;
}
append_bits_128 :: proc(buf: []byte, u: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits");
}
neg: bool;
a: [140]byte;
i := len(a);
u, neg = is_integer_negative_128(u, is_signed, bit_size);
b := u128(base);
for u >= b {
i-=1; a[i] = digits[u % b];
u /= b;
}
i-=1; a[i] = digits[u % b];
if .Prefix in flags {
ok := true;
switch base {
case 2: i-=1; a[i] = 'b';
case 8: i-=1; a[i] = 'o';
case 10: i-=1; a[i] = 'd';
case 12: i-=1; a[i] = 'z';
case 16: i-=1; a[i] = 'x';
case: ok = false;
}
if ok {
i-=1; a[i] = '0';
}
}
switch {
case neg:
i-=1; a[i] = '-';
case .Plus in flags:
i-=1; a[i] = '+';
case .Space in flags:
i-=1; a[i] = ' ';
}
out := a[i:];
copy(buf, out);
return string(buf[0:len(out)]);
}