1

I believe FFFFFFFF is -1 because of Two's Complement.

I tried to convert Hex String into Integer, but I got the error.

Here is the code I have tried.

  • code
// Extension functions
val Int.asByteArray get() =
    byteArrayOf(
            (this shr 24).toByte(),
            (this shr 16).toByte(),
            (this shr 8).toByte(),
            this.toByte())

val Int.asHex get() = this.asByteArray.asHexUpper

// Main Code
fun main()
{
    println((-1).asHex)
    println("FFFFFFFF".toInt(16))
}
  • Result
FFFFFFFF
Exception in thread "main" java.lang.NumberFormatException: For input string: "FFFFFFFF"
    at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.base/java.lang.Integer.parseInt(Integer.java:652)
    at java.base/java.lang.Integer.parseInt(Integer.java:770)

Is this intended? or Is this error?

If this is intended, then what should I do?

Pemassi
  • 612
  • 1
  • 7
  • 20
  • Does this answer your question? [2's complement hex number to decimal in java](https://stackoverflow.com/questions/6699275/2s-complement-hex-number-to-decimal-in-java) – Zoe Apr 09 '21 at 09:22
  • TL;DR: Java (and by extension Kotlin) requires a signed value for toInt. That also means that -1 in decimal is -1 in hex, because `parseInt()` doesn't actually operate on two's complement. Another dupe target ([this one](https://stackoverflow.com/a/27332573); can't add both because I don't have a Kotlin hammer yet) mentions `Integer.parseUnsignedInt()`, which does return -1 for `"FFFFFFFF"` – Zoe Apr 09 '21 at 09:27
  • println(java.lang.Long.parseLong("FFFFFFFF", 16)) returns 4294967295 and this value fits to Long – Sergey Afinogenov Apr 09 '21 at 09:35
  • @Zoe This is quite interesting...! These codes are working. `java.lang.Long.parseLong("FFFFFFFF", 16).toInt()`, `Integer.parseUnsignedInt("FFFFFFFF", 16)`. Second one is saying "Parsing Unsigned Integer", but it's returning signed integer... – Pemassi Apr 09 '21 at 10:44
  • Probably assumes the integer input is unsigned and parses it by using two's complement instead of looking for `-` at the start of the string. Also worth noting that Java doesn't have a concept of unsigned integers (... at least last I checked, but I'm about 5 versions out of date atm, so /shrug). The thing about two's complement is that MIN_VALUE = MAX_VALUE + 1 in binary form, and from the documentation of parseUnsignedInt: – Zoe Apr 09 '21 at 10:48
  • "Parses the string argument as an unsigned integer in the radix specified by the second argument. **An unsigned integer maps the values usually associated with negative numbers to positive numbers larger than MAX_VALUE**". In a normal, signed int, these are then wrapped around to negative values in a two's complement-compatible way – Zoe Apr 09 '21 at 10:49
  • “I believe FFFFFFFF is -1 because of Two's Complement.”  Strictly speaking, that's never true.  A four-byte field can either be signed (in which case it can't hold a value representing FFFFFFFF) or unsigned (in which case it can't hold a value representing -1).  A wider value could hold both, but they would be distinct.  It's true that if you were to _reinterpret_ the unsigned value FFFFFFFF as signed, then you'd get -1 — but that's not the same thing. – gidds Apr 09 '21 at 10:54

2 Answers2

1

Your code presumably can't parse 4 bytes (32 bit) to a signed integer, since this only contains 31 bits as one bit is reserved for the sign.

A solution would be to parse it into an unsigned integer (as Some random IT boy stated) and then convert the (16bit-)UInt to a (16bit-)Int:

fun main()
{
    val u = "FFFFFFFF".toUInt(16) //16 as it is base 16
    println(u) //prints "4294967295"
    val v = u.toInt()
    println(v) //prints "-1"
}

Use with caution as this will not work when your data is not of length 32: You can not parse FFFF and expect it to be -1 as it would be parsed to 0000FFFF which is equal to 65535.

For data lengths of 1, 2 or 8 bytes, look into the data types Byte, Short or Long and their respective Functions:

    val u = "FFFFFFFF".toULong(16)
    println(u) //prints 4294967295
    val v = u.toLong()
    println(v) //still prints 4294967295
Torge Rosendahl
  • 482
  • 6
  • 17
0

It's -1 because the first bit of the 4 byte memory block decides the sign of the number. In this case since it's all 1's then it's a negative number. Then the value of this memory block is the Two's complement because we're dealing with a signed integer

The problem you're facing is not Kotlin specific: checkout this question from StackOverflow

Java has also trouble parsing such string to an Integer, but a Long has no trouble; because it has 4 more bytes to spare, and in fact, if you'd write the value literal 0xFFFFFFFF Kotlin grabs it as if it's a Long:

enter image description here

A quick fix for these could be to use the unsigned counterpart of the int UInt

"FFFFFFFF".toUInt(16) // 4294967295

Or just use a Long

In https://kotlinlang.org/

Kotlinglang.org

Some random IT boy
  • 7,569
  • 2
  • 21
  • 47