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.

Othman H
  • 75
  • 2
  • 9
  • You can try subtracting 65536 from your logic and shift result if negative, or are you looking for a one liner? – Ray Toal Jun 22 '18 at 03:33
  • 1
    Also this might help https://stackoverflow.com/questions/5625573/byte-array-to-short-array-and-back-again-in-java – Ray Toal Jun 22 '18 at 03:34
  • I don't understand the need of the byte & 0xff part. The result of that operation is always the first operand... – FinnTheHuman Jun 22 '18 at 03: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. – Othman H Jun 22 '18 at 03: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. – FinnTheHuman Jun 22 '18 at 03:48

3 Answers3

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;
Radiodef
  • 37,180
  • 14
  • 90
  • 125
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.

FinnTheHuman
  • 1,115
  • 13
  • 29
  • 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 https://stackoverflow.com/q/11380062/2891664. Java always promotes `byte` to `int` (with sign extension) when doing any kind of arithmetic. – Radiodef Jun 22 '18 at 04:23
  • @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. – FinnTheHuman Jun 22 '18 at 05:25
  • See for example: https://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?) – Radiodef Jun 22 '18 at 14:36
0

kotlin way

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

        return (high.toInt() shl 8) or low.toInt()
Fortran
  • 2,218
  • 2
  • 27
  • 33