0

Long story short, I am trying to convert strings of hex values to signed 2's complement integers. I was able to do this in a single line of code in Swift, but for some reason I can't find anything analogous in Kotlin. String.ToInt or String.ToUInt just give the straight base 16 to base 10 conversion. That works for some positive values, but not for any negative numbers.

How do I know I want the signed 2's complement? I've used this online converter and according to its output, what I want is the decimal from signed 2's complement, not the straight base 16 to base 10 conversion that's easy to do by hand.

So, "FFD6" should go to -42 (correct, confirmed in Swift and C#), and "002A" should convert to 42.

I would appreciate any help or even any leads on where to look. Because yes I've searched, I've googled the problem a bunch and, no I haven't found a good answer.

I actually tried writing my own code to do the signed 2's complement but so far it's not giving me the right answers and I'm pretty at a loss. I'd really hope for a built in command that does it instead; I feel like if other languages have that capability Kotlin should too.

squiiidz
  • 83
  • 1
  • 12

1 Answers1

3

For 2's complement, you need to know how big the type is.

Your examples of "FFD6" and "002A" both have 4 hex digits (i.e. 2 bytes).  That's the same size as a Kotlin Short.  So a simple solution in this case is to parse the hex to an Int and then convert that to a Short.  (You can't convert it directly to a Short, as that would give an out-of-range error for the negative numbers.)

"FFD6".toInt(16).toShort() // gives -42

"002A".toInt(16).toShort() // gives 42

(You can then convert back to an Int if needed.)

You could similarly handle 8-digit (4-byte) values as Ints, and 2-digit (1-byte) values as Bytes.

For other sizes, you'd need to do some bit operations.  Based on this answer for Java, if you have e.g. a 3-digit hex number, you can do:

("FD6".toInt(16) xor 0x800) - 0x800 // gives -42

(Here 0x800 is the three-digit number with the top bit (i.e. sign bit) set.  You'd use 0x80000 for a five-digit number, and so on.  Also, for 9–16 digits, you'd need to start with a Long instead of an Int.  And if you need >16 digits, it won't fit into a Long either, so you'd need an arbitrary-precision library that handled hex…)

gidds
  • 16,558
  • 2
  • 19
  • 26
  • Thank you! So if I had, say, 0xFFD6, I wouldn't be able to do "0xFFD6".toInt(16).toShort() to get -42, right? Could I do "0xFFD6".toInt(16).toLong() ? – squiiidz Apr 19 '21 at 17:00
  • Try it and see!  You can run `kotlinc`, and type programs and expressions straight into the REPL.  Typing in ` "0xFFD6".toInt(16).toShort()` gives a NumberFormatException, as it can't parse the `0x`.  (I'm not aware of any built-in functions which can.)  And `"FFD6".toInt(16).toLong()` doesn't do what you want (giving 65494); it's the truncation to `Short` that sorts out the sign. – gidds Apr 19 '21 at 17:19