Is there a way to reinterpret a signed integer to ...
# getting-started
c
Is there a way to reinterpret a signed integer to unsigned without changing its in-memory representation? (and the other way around as well)
p
Isn't UInt just a value class wrapping an Int?
c
It is, but .toUInt will change the bits so the value stays the same. I'm searching for a function that converts between them, but keeping the bits the same (so the value will be interpreted as something else)
p
Copy code
/**
 * Converts this [Int] value to [UInt].
 *
 * If this value is positive, the resulting `UInt` value represents the same numerical value as this `Int`.
 *
 * The resulting `UInt` value has the same binary representation as this `Int` value.
 */
@SinceKotlin("1.5")
@WasExperimental(ExperimentalUnsignedTypes::class)
@kotlin.internal.InlineOnly
public inline fun Int.toUInt(): UInt = UInt(this)
It's documented as having the same binary representation (as indeed a uint32 and int32 have the same binary representation with the only difference being how the MSB is treated)
c
What about the reverse operation?
p
c
Not if the first bit is set
There's a chance I'm confused
p
So long as the value fits in an Int, its numeric value will be unchanged when converting. If the value is greater than the max signed int then it will still have the same binary representation but will have a negative value.
Signed Ints are represented using 2's complement
c
I'm confused by this:
Copy code
val counter: UInt 

counter        // 4294967295
    .toULong() // 4294967295
    .shl(22)   // 18446744069414584320
    .toLong()  // -4294967296
Here, I don't want
.toLong()
to change the value, I want to keep the same one (so I do want to change the binary representation, sorry)
But actually maybe this is right?
The actual use-case: I have two
UInt
that I must combine into a single
Long
(one
UInt
is the 4 highest bytes, the other is the other four), and later extract both of them.
p
If the MSB of the high UInt is set then you'll end up with a negative Long but the binary value will be as expected
Also you'd want to shl(32)
c
yes, sorry I made a mistake while copying it here
Ok so this seems to be the correct roundtrip:
Copy code
val actual = counter        
			.also { println(it) }
			.toULong()
			.also { println(it) }
			.shl(32)
			.also { println(it) }
			.toLong()
			.also { println(it) }
			.toULong()
			.also { println(it) }
			.shr(32)
			.also { println(it) }
			.toUInt()
			.also { println(it) }
k
You don't have to use ULong or UInt. You can just use signed types, and it'll do what you expect if you use
shl
and
ushr
on those.
c
I do have to use
UInt
for the counter and
Long
for the full variable here, for reasons that are elsewhere in the program.
l
You can disassemble the bytecode and you'll see that
.toUInt()
(and related functions) does not change the values at all. It's just compile-time typing. If your two UInts are in the variables
a
and
b
, the solution to your problem is:
Copy code
(a.toLong() shl 32) or b.toLong()
Actually, the byte code will call the value class constructor, but that one just returns the value unchanged, so it's inlined by the compiler later.
c
True, and that's true for all value classes, but that doesn't necessarily mean the value is unchanged. value classes can override operators to do something else even if the value is the same, so the fact that the constructor does nothing is not enough guarantee to know what the value is truly the same.
l
Fair enough, but it is true in this case.
You can implement two functions:
fun foo(a: Int) = a+1
and the other one being:
fun bar(a: UInt) = a+1U
You can see they compile to the same thing.