Othman H
Othman H

Reputation: 71

Convert two bytes to a signed integer in java

Question in short

Is there a way to read two bytes as a signed integer?

Details & example

Given two bytes in java, each represents an integer, we can convert them to the int value they represent together by simply:

byte[] byteArray = new byte[4];
byteArray[0] = 0x00;
byteArray[1] = 0x00;
byteArray[2] = .. //a byte representing an integer
byteArray[3] = .. //a byte representing an integer

int i = ((byteArray[2] & 0xff) << 8) | (byteArray[3] & 0xff); 

It works perfectly when the two bits represent positive integers. But it fails when the two integers are negative. For example when:

byteArray[2] = 0xff; // -1
byteArray[3] = 0xf9; // -7

I get:

i = 65529;

which is incorrect. It should just be -8 which is 0xf8.

I tried using ByteBuffer:

byte[] array = new byte[4];
array[0] = 0x00;
array[1] = 0x00;
array[2] = 0xff;
array[3] = 0xf9;

ByteBuffer buffer = ByteBuffer.wrap(array);
int i = buffer.getInt();

Didn't work. Got the same result:

i = 65529

Those are only examples. There will be more bytes and they will be representing positive and negative integers.

Is there a way to read two bytes as a signed integer and get the correct result?

Thanks in advance.

Upvotes: 2

Views: 4188

Answers (3)

Fortran
Fortran

Reputation: 2336

kotlin way

        val low: UByte = bytes[3].toUByte()
        val high: UByte = bytes[4].toUByte()

        return (high.toInt() shl 8) or low.toInt()

Upvotes: 0

Radiodef
Radiodef

Reputation: 37835

In your case, you can just remove the bitwise & from the high byte:

int i = (byteArray[2] << 8) | (byteArray[3] & 0xff);

& 0xff was undoing the sign extension that you wanted. You still need it on the low byte.

Two's complement sign extension works like this:

  1. If the most-significant bit of the smaller size number is set,

    //    v
        0b1000000_00000000
    
  2. Fill the new bits above the old most-significant bit with 1s:

    //    vvvvvvvv vvvvvvvv
        0b11111111_11111111_1000000_00000000
    

Java does it automatically any time a byte or short is converted to int or long, and the purpose of & 0xFF on a byte is to undo the automatic sign extension.

If you didn't have access to the bytes, you could do sign extension yourself using the arithmetic right-shift:

i = (i << 16) >> 16;

Or casting to a short:

i = (short) i;

Or a variety of if tests such as:

if ((i & 0x80_00) != 0) // Is the MSB of the high byte set?
    i |= 0xFF_FF_00_00; // Fill the rest with 1s.

And:

if (i > 32767)
    i -= 65536;

Upvotes: 3

FinnTheHuman
FinnTheHuman

Reputation: 1155

Two bytes as a signed integer:

public class MyClass {
    public static void main(String args[]) {
        byte h = (byte)0xff;
        byte l = (byte)0xf9;

        int i = (short) ((h << 8) | l);
        System.out.println(i);
    }
}

(I'll paste the comment I made under your question here):

Use a short instead, because the leftmost bit of your int is 0, therefore it is a positive number. But if you use a short, then you'll get the negative value you want, because the type short only have 2 bytes, then the leftmost bit will be the leftmost 1 in 0xFF, making it a negative number.

Upvotes: 2

Related Questions