207 lines
4.7 KiB
C++
207 lines
4.7 KiB
C++
/*
|
|
We use two's complement
|
|
|
|
- Adding the value, sub is similar
|
|
Adding a positive number to a negative number is actually subtracting the value.
|
|
Last bit is a biggest possible negative number for that bit range.
|
|
0111 1111 => 127
|
|
1111 1111 => -128 - 127 = 1
|
|
|
|
1000 0000 => -128
|
|
if we add 4 we get
|
|
1000 0100 => -124
|
|
|
|
- Negating the value
|
|
1000 0001 => -127
|
|
flip bits
|
|
0111 1110 => 126
|
|
we need to add 1
|
|
0111 1111 -> 127
|
|
|
|
- Division
|
|
We don't need full 128 bit divide
|
|
we just need to make sure we always divide
|
|
smaller then U64MAX and -U64MAX
|
|
10 / -1 = -10
|
|
10 / 1 = 10
|
|
-10 / -1 = 10
|
|
-10 / 1 = 10
|
|
*/
|
|
|
|
struct S128{
|
|
U64 hi, lo;
|
|
constexpr S128 max(){return {S64MAX, U64MAX};}
|
|
constexpr S128 min(){return {(U64)S64MIN, 0};}
|
|
#define S128MAX S128::max()
|
|
#define S128MIN S128::min()
|
|
};
|
|
|
|
function S64
|
|
is_negative(S128 v){
|
|
S64 result = v.hi >> 63;
|
|
return result;
|
|
}
|
|
|
|
function S64
|
|
sign(S128 val){
|
|
S64 result = is_negative(val) ? -1 : 1;
|
|
return result;
|
|
}
|
|
|
|
function B32
|
|
has_value_bigger_then_64bit(S128 v){
|
|
U64 hi = v.hi << 1; // clear sign bit
|
|
B32 result = hi != 0;
|
|
return result;
|
|
}
|
|
|
|
function S128
|
|
s128_from_s64(S64 value){
|
|
S128 result = {value < 0 ? ~0 : 0ull, (U64)value};
|
|
return result;
|
|
}
|
|
|
|
function S128
|
|
s128_from_u64(U64 value){
|
|
S128 result = {0, value};
|
|
return result;
|
|
}
|
|
|
|
function S128
|
|
operator+(S128 a, S128 b){
|
|
U64 lo = a.lo + b.lo;
|
|
U64 hi = a.hi + b.hi + (a.lo > lo); // (a.lo > lo) is carry
|
|
return {hi,lo};
|
|
}
|
|
|
|
function S128
|
|
operator-(S128 a, S128 b){
|
|
U64 lo = a.lo - b.lo;
|
|
U64 hi = a.hi - b.hi - (a.lo < lo); // (a.lo < lo) is carry
|
|
return {hi,lo};
|
|
}
|
|
|
|
force_inline U64 lo32(U64 a){return a & 0xffffffff;}
|
|
force_inline U64 hi32(U64 a){return a >> 32;}
|
|
force_inline S128 operator~(S128 a){return {~a.hi, ~a.lo};}
|
|
force_inline S128 operator^(S128 a, S128 b){return {a.hi ^ b.hi, a.lo ^ b.lo};}
|
|
force_inline S128 operator&(S128 a, S128 b){return {a.hi & b.hi, a.lo & b.lo};}
|
|
force_inline S128 operator|(S128 a, S128 b){return {a.hi | b.hi, a.lo | b.lo};}
|
|
|
|
function S128
|
|
operator-(S128 a){
|
|
a = (~a) + s128_from_u64(1);
|
|
return a;
|
|
}
|
|
|
|
function S128
|
|
s128_div_hack(S128 a, S128 b){
|
|
// @hack: only values less then U64MAX -U64MAX
|
|
S64 sign = 1;
|
|
if(is_negative(a)){
|
|
sign *= -1;
|
|
a = -a;
|
|
}
|
|
if(is_negative(b)){
|
|
sign *= -1;
|
|
b = -b;
|
|
}
|
|
assert(a.hi == 0 && b.hi == 0);
|
|
U64 division = a.lo / b.lo;
|
|
S128 result = {0, division};
|
|
if(sign == -1) result = -result;
|
|
return result;
|
|
}
|
|
|
|
function S128
|
|
u64_mul(U64 u, U64 v){
|
|
U64 u_lo = lo32(u);
|
|
U64 v_lo = lo32(v);
|
|
U64 u_hi = hi32(u);
|
|
U64 v_hi = hi32(v);
|
|
|
|
U64 t1 = u_lo * v_lo;
|
|
U64 t2 = (u_hi * v_lo) + hi32(t1);
|
|
U64 t3 = (u_lo * v_hi) + lo32(t2);
|
|
|
|
U64 lo = (t3 << 32) + lo32(t1);
|
|
U64 hi = (u_hi * v_hi) + hi32(t2) + hi32(t3);
|
|
return {hi,lo};
|
|
}
|
|
|
|
function S128
|
|
operator*(S128 a, S128 b){
|
|
S128 c = u64_mul(a.lo, b.lo);
|
|
c.hi += (a.hi * b.lo) + (a.lo * b.hi);
|
|
return c;
|
|
}
|
|
|
|
function B32
|
|
operator==(S128 a, S128 b){
|
|
B32 result = (a.lo == b.lo) && (a.hi == b.hi);
|
|
return result;
|
|
}
|
|
|
|
function void
|
|
test_big_int(){
|
|
S128 v1 = s128_from_s64(-1);
|
|
assert(v1.hi == U64MAX && v1.lo == U64MAX);
|
|
assert(is_negative(v1));
|
|
assert(sign(v1) == -1);
|
|
|
|
S128 v2 = s128_from_u64(U64MAX) + s128_from_u64(1);
|
|
assert(v2.lo == 0 && v2.hi == 1);
|
|
|
|
S128 v3 = s128_from_u64(U64MAX) + s128_from_u64(100);
|
|
assert(v3.lo == 99 && v3.hi == 1);
|
|
|
|
// S64 s64_max = S64MAX;
|
|
S128 v4 = s128_from_s64(S64MIN) + s128_from_u64(100);
|
|
assert((v4.lo - S64MAX) == 101); // 101 cause S64MIN is larher by 1
|
|
|
|
S128 v5 = {1, 0};
|
|
assert(has_value_bigger_then_64bit(v5));
|
|
S128 v6 = v5 - s128_from_u64(1);
|
|
assert(v6.lo == U64MAX && v6.hi == 0);
|
|
|
|
{
|
|
S128 v7 = u64_mul(S64MAX, S64MAX);
|
|
assert(v7.hi == 0x3fffffffffffffff && v7.lo == 0x0000000000000001);
|
|
}
|
|
|
|
{
|
|
S128 v7 = u64_mul(S64MIN, S64MIN);
|
|
assert(v7.hi == 0x4000000000000000 && v7.lo == 0);
|
|
assert(has_value_bigger_then_64bit(v7));
|
|
}
|
|
|
|
{
|
|
S128 v7 = u64_mul(U64MAX, U64MAX);
|
|
assert(v7.hi == 0xfffffffffffffffe && v7.lo == 0x0000000000000001);
|
|
assert(has_value_bigger_then_64bit(v7));
|
|
}
|
|
|
|
{
|
|
S128 v7 = u64_mul(52242, 2);
|
|
assert(v7.lo == 52242*2);
|
|
assert(!has_value_bigger_then_64bit(v7));
|
|
}
|
|
|
|
{
|
|
S128 v7 = u64_mul(0, 0);
|
|
assert(v7 == s128_from_u64(0));
|
|
assert(!has_value_bigger_then_64bit(v7));
|
|
}
|
|
|
|
S128 v8 = s128_div_hack(-s128_from_u64(U64MAX), s128_from_s64(-1));
|
|
assert(v8.lo == U64MAX && v8.hi == 0);
|
|
|
|
S128 v9 = s128_div_hack(s128_from_u64(S64MAX), s128_from_s64(S64MAX));
|
|
assert(v9 == s128_from_s64(1));
|
|
|
|
S128 v10 = s128_div_hack(-s128_from_u64(S64MAX), s128_from_s64(S64MAX));
|
|
assert(v10 == -s128_from_s64(1));
|
|
|
|
S128 v11 = s128_div_hack(s128_from_s64(S64MIN), s128_from_s64(S64MAX));
|
|
assert(v11 == -s128_from_u64(1));
|
|
} |