Odd Behavior When Java Converts Int to Byte

Odd behavior when Java converts int to byte?

In Java, an int is 32 bits. A byte is 8 bits .

Most primitive types in Java are signed, and byte, short, int, and long are encoded in two's complement. (The char type is unsigned, and the concept of a sign is not applicable to boolean.)

In this number scheme the most significant bit specifies the sign of the number. If more bits are needed, the most significant bit ("MSB") is simply copied to the new MSB.

So if you have byte 255: 11111111
and you want to represent it as an int (32 bits) you simply copy the 1 to the left 24 times.

Now, one way to read a negative two's complement number is to start with the least significant bit, move left until you find the first 1, then invert every bit afterwards. The resulting number is the positive version of that number

For example: 11111111 goes to 00000001 = -1. This is what Java will display as the value.

What you probably want to do is know the unsigned value of the byte.

You can accomplish this with a bitmask that deletes everything but the least significant 8 bits. (0xff)

So:

byte signedByte = -1;
int unsignedByte = signedByte & (0xff);

System.out.println("Signed: " + signedByte + " Unsigned: " + unsignedByte);

Would print out: "Signed: -1 Unsigned: 255"

What's actually happening here?

We are using bitwise AND to mask all of the extraneous sign bits (the 1's to the left of the least significant 8 bits.)
When an int is converted into a byte, Java chops-off the left-most 24 bits

1111111111111111111111111010101
&
0000000000000000000000001111111
=
0000000000000000000000001010101

Since the 32nd bit is now the sign bit instead of the 8th bit (and we set the sign bit to 0 which is positive), the original 8 bits from the byte are read by Java as a positive value.

What algorithm does java use to typecast int to byte?

It takes the least significant 8 bytes of the binary representation of 400 and assigns them to the byte variable.

The binary representation of the int 400 is :

00000000000000000000000110010000
--------

When taking the low 8 bits, you get 10010000, which is the binary representation of -112.

convert integer to byte if the integer is less than 16

No, you won't get unexpected behaviour converting a byte between 0 and 15 into an int on different platforms. One of the strengths of Java is that it precisely defines what happens with such conversions, so that they are always platform independent.

Weird behaviour of bit-shifting with byte in Java

This happens exactly because byte is promoted to int prior performing bitwise operations. int -128 is presented as:

11111111 11111111 11111111 10000000

Thus, shifting right to 7 or 8 bits still leaves 7-th bit 1, so result is narrowed to negative byte value.

Compare:

System.out.println((byte) (b >>> 7));           // -1
System.out.println((byte) ((b & 0xFF) >>> 7)); // 1

By b & 0xFF, all highest bits are cleared prior shift, so result is produced as expected.

Type casting into byte in Java

The byte type is encoded on 8 bits, so it takes its values between -128 and 127. In your case, casting by byte is the same as computing a modulo and rounding to an int. Try the following code, the output is the same:

int a = 257;
double d = 323.142;
System.out.println(a % 128);
System.out.println((int) d % 128);

Java byte type is weird?

b6 does work due to compile-time narrowing of literal constants. b7 does not work because compile-time narrowing is limited to all primitives but long (kind of strange, no idea why)

The interesting part is §5.2 of the JLS:

In addition, if the expression is a constant expression (§15.28) of type byte, short, char or int :

A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.
A narrowing primitive conversion followed by a boxing conversion may be used if the type of the variable is :

- Byte and the value of the constant expression is representable in the
type byte.

- Short and the value of the constant expression is representable in
the type short.

- Character and the value of the constant expression is representable in the type char.

If the type of the expression cannot be converted to the type of the variable by a conversion permitted in an assignment context, then a compile-time error occurs.

No idea why i does not work though - widening should work just fine and in fact, the compiler should generate something like Integer.valueOf((byte)3); anyhow. Using the explicit call works as expected, i.e. widening is happening.

Interestingly enough using the eclipse Java compiler Integer i = (byte) 3; compiles just fine, which leads me to believe you just found a bug in javac - congratulations! (well either that or a bug in the eclipse compiler; but eclipse's behavior seems the correct one to me). FWIW I've reported the bug against javac to oracle..

Finding the right part in the JLS was less work than formatting this that it's somewhat readable - so probably easier if you follow the link instead.



Related Topics



Leave a reply



Submit