What is the rusticle way to to represent the an i64 [-9223372036854775808, 9223372036854775807] into the u64 domain [0, 18446744073709551615]. So for example 0 in i64 is 9223372036854775808 in u64.
Here is what I have done.
let x: i64 = -10;
let x_transform = ((x as u64) ^ (1 << 63)) & (1 << 63) | (x as u64 & (u64::MAX >> 1));
let x_original = ((x_transform as i64) ^ (1 << 63)) & (1 << 63) | (x_transform & (u64::MAX >> 1)) as i64;
println!("x_transform {}", x_transform);
println!("x_original {} {}", x_original, x_original == x);
yielding
x_transform 9223372036854775798
x_original -10 true
Is there a built in way to do this, because it seems too verbose, and error prone?
From a performance view it doesn't really matter how you write it, a quick check on godbot shows both the wrapping and the bit shifty versions compile to the same machine code. But I'd argue the variants with wrapping are way more readable and convey the intent better.
pub fn wrap_to_u64(x: i64) -> u64 {
(x as u64).wrapping_add(u64::MAX/2 + 1)
}
pub fn wrap_to_i64(x: u64) -> i64 {
x.wrapping_sub(u64::MAX/2 + 1) as i64
}
pub fn to_u64(x: i64) -> u64 {
((x as u64) ^ (1 << 63)) & (1 << 63) | (x as u64 & (u64::MAX >> 1))
}
pub fn to_i64(x: u64) -> i64 {
((x as i64) ^ (1 << 63)) & (1 << 63) | (x & (u64::MAX >> 1)) as i64
}
example::wrap_to_u64:
movabs rax, -9223372036854775808
xor rax, rdi
ret
example::wrap_to_i64:
movabs rax, -9223372036854775808
xor rax, rdi
ret
example::to_u64:
movabs rax, -9223372036854775808
xor rax, rdi
ret
example::to_i64:
movabs rax, -9223372036854775808
xor rax, rdi
ret
The lesson to learn is that unless you have a very specific optimization probably the compiler will outperform you.