Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bitwise operators in java only for integer and long?

I wrote following code in Eclipse:

byte b = 10;
/* some other operations */
b = ~b;

Eclipse wanted a cast to byte in the line of the bitwise complement. It said: "Type mismatch: cannot convert from int to byte". I also tried this with other bitwise operations and on other integral types. It was with short and char the same. Only long and integer could use bitwise operations.

Is there a reason for this?

like image 438
Michael Langhammer Avatar asked Dec 13 '13 23:12

Michael Langhammer


2 Answers

Unary (such as ~) and binary operators in Java subject their operands to "unary numeric promotion" (JLS, Section 5.6.1) and "binary numeric promotion" (JLS, Section 5.6.2), respectively, fancy terms for "promote things to at least int first".

Specifically, for unary numeric promotion, quoting the JLS section linked above:

Some operators apply unary numeric promotion to a single operand, which must produce a value of a numeric type:

and

... if the operand is of compile-time type byte, short, or char, it is promoted to a value of type int by a widening primitive conversion (§5.1.2).

(Binary numeric promotion is similar, operating on both operands.)

So, even if b is a byte, ~b is an int, because b's value was promoted to an int first.

The solution: cast it back to a byte:

b = (byte) (~b);

Why, Java?

That leaves the question, why? It seems for the operators I can find, the JVM bytecode instructions for operating on bytes, shorts, and chars simply do not exist. For example, the unary bitwise complement operator you're using (~) is implemented as an "XOR" operation with -1 (all bits set). From that link:

tempSpock &= ~mask;

becomes

25 iload_2 // Push local variable 2 (mask).
26 iconst_m1 // Push -1.
27 ixor // Bitwise EXCLUSIVE-OR top two ints: ~mask

However, I can only find instructions for XOR (and for other unary and binary operators too) for ints and longs (float and double version exist for other operators where appropriate).

So, Java must perform those promotions because there are no bytecode instructions for performing those operations on bytes, shorts, or chars.

Why not, JVM?

That brings up another question: Why doesn't the JVM support such bytecode instructions? The answer appears to be, "Because there would be too many to encode them all in a one-byte instruction set." According to the JVM Specification, Section 2.11.1,

Given the Java Virtual Machine's one-byte opcode size, encoding types into opcodes places pressure on the design of its instruction set. If each typed instruction supported all of the Java Virtual Machine's run-time data types, there would be more instructions than could be represented in a byte. Instead, the instruction set of the Java Virtual Machine provides a reduced level of type support for certain operations. In other words, the instruction set is intentionally not orthogonal. Separate instructions can be used to convert between unsupported and supported data types as necessary.

(emphasis mine)

In conclusion, the JVM's one-bytecode instruction set precludes the bytecode instructions for most operations on bytes, chars, and shorts, necessitating unary numeric promotion and binary numeric promotion.

like image 112
rgettman Avatar answered Oct 06 '22 03:10

rgettman


Yes, types smaller than int undergo promotion when used as operands to most operators. They are effectively cast to int first. Therefore, in your code above, the type of the result is int. See this section of the JLS for the full details.

As for why Java does this, I'm not sure. But one plausible reason is that C does this, and maintaining familiar semantics was probably a language design objective.

like image 25
Oliver Charlesworth Avatar answered Oct 06 '22 01:10

Oliver Charlesworth