1

I need to convert arbitrary sized byte arrays to a short/int/long.
This means I could receive 3 bytes to be converted to int:

final byte[] bytes = { 0b0000, 0b0000, 0b1111 }; // 15 - big endian
final int value = doConversion(bytes);

Thus, I'm trying to come up with a generic function.


ByteBuffer conversions works great when you have arrays of a size that exactly represent a short, int, long value. But what if I have an int represented as a single byte?

final byte[] bytes = { 0b1111 }; // 15

It seems converting such a byte array to an int using a ByteBuffer requires resizing the array and padding it.
Things get even more complicated with a negative value, as the padding needs to be done with the most significant bit.

final byte[] bytes = { (byte) 0b11110001 }; // -15 stored as two's complement

Is there an easier way to accomplish this task, or should I just use custom code?
An example could be, in Kotlin, using extension functions:

fun ByteArray.toShort(byteOrder: ByteOrder = LITTLE, signed: Boolean = true): Short =
    toInteger(Short.SIZE_BYTES, byteOrder, signed).toShort()

fun ByteArray.toInt(byteOrder: ByteOrder = LITTLE, signed: Boolean = true): Int =
    toInteger(Int.SIZE_BYTES, byteOrder, signed).toInt()

fun ByteArray.toLong(byteOrder: ByteOrder = LITTLE, signed: Boolean = true): Long =
    toInteger(Long.SIZE_BYTES, byteOrder, signed)

private fun ByteArray.toInteger(
     typeBytes: Int /* 2, 4, 8 */,
     byteOrder: ByteOrder /* little, big */, 
     signed: Boolean,
): Long {
    // Checks omitted...

    // If the byte array is bigger than the type bytes, it needs to be truncated
    val bytes =
        if (size > typeBytes) {
            if (byteOrder == LITTLE) {
                copyOf(typeBytes)
            } else {
                copyOfRange(size - typeBytes, size)
            }
        } else {
            copyOf()
        }

    val negative =
        signed && (this[if (byteOrder == LITTLE) bytes.size - 1 else 0]).toInt() and 0b1000000 != 0

    if (!negative) {
        return bytes.absoluteToLong(byteOrder)
    }

    // The number is stored as two's complement.
    // Thus we invert each byte and then sum 1 to obtain the absolute value
    for (i in bytes.indices) {
        bytes[i] = bytes[i].inv()
    }

    return -(bytes.absoluteToLong(byteOrder) + 1)
}

private fun ByteArray.absoluteToLong(byteOrder: ByteOrder): Long {
    var result = 0L
    var shift = 8 * (size - 1)
    val range =
        if (byteOrder == LITTLE) {
            size - 1 downTo 0
        } else {
            0 until size
        }

    for (i in range) {
        result = (this[i].toInt() and 0b11111111).toLong() shl shift or result
        shift -= 8
    }

    return result
}

1 Answer 1

2

The BigInteger class has a constructor that's convenient for this use case. Example:

byte[] bytes = { 0b1111 }; 
int value = new BigInteger(bytes).intValue(); 

It does "sign-extension" for you: if the first byte value in the array is negative, the end result is negative.

It has also methods to retrieve the value as the other number types (short, long, ...).

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks! I assume the accepted bytes need to be always in big endian. Also (maybe off topic), what about unsigned values? e.g. 255 represented as a single byte.
Noticed the update. Also maybe to add how to consider signed/unsigned values. e.g. byte[] bytes = { (byte) 0b11111111 } // 255 as a single byte would be converted using new BigInteger(bytes).intValue() & 0xFF.
correct, it assumes a big endian order. As for unsigned, I haven't tested it but adding a zero byte as padding should work.
But applying the two's complements will simply change the sign. Example, new BigInteger(new byte[] {(byte) 0b11111111} /* 255 */).intValue() will return -1, and applying two's complement will simply make it 1. Maybe I misunderstood your point
Oh, ok, I see you redacted. No problem!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.