2

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.

5
  • You can try subtracting 65536 from your logic and shift result if negative, or are you looking for a one liner? Commented Jun 22, 2018 at 3:33
  • 1
    Also this might help stackoverflow.com/questions/5625573/… Commented Jun 22, 2018 at 3:34
  • I don't understand the need of the byte & 0xff part. The result of that operation is always the first operand... Commented Jun 22, 2018 at 3:37
  • @RayToal I need a way to treat both negative and positive bytes. I only provided an example of a case where the two ints are negative. There will be other cases ofcourse. Commented Jun 22, 2018 at 3:43
  • 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 since shorts only have 2 bytes, the leftmost bit will be the leftmost 1 in 0xFF, making it a negative number. Commented Jun 22, 2018 at 3:48

3 Answers 3

3

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;
Sign up to request clarification or add additional context in comments.

Comments

2

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.

3 Comments

You still need to do (l & 0xFF). Otherwise when l is negative, you'll end up with 1s in place of the high byte. See also stackoverflow.com/q/11380062/2891664. Java always promotes byte to int (with sign extension) when doing any kind of arithmetic.
@Radiodef I'm not sure I understand. I think we're talking about the numbers on a bit manipulation level, not arithmetic, and the OP wants to get the bits of each byte together, one after the other (one high and one low), no matter what if those bits represent a negative or positive number when interpreted as a byte. And also that's a very strange operation, since byte b; b & 0xFF always yields an int with the exact same bits as b, padded with 0s to the right, which is the same as simply casting the byte to an int, Maybe I'm missing something.
See for example: ideone.com/bqMc16. Again, because Java converts byte to int with sign extension, meaning if the MSB of the byte is set, then the int value is filled with 1s in bits 8-31. We use & 0xFF to remove the 1s from sign extension. (How do you not know about sign extension, when the short to int conversion in your answer relies on it to work?)
0

kotlin way

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

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

Comments

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.