I encountered misunderstanding of primitive promotion in the next code snippet.
byte a = 2;
int b = a >> 4L;
What would I expect?
long b = (int)a >> 4L;
long b = a >> 4L;
int b = a >> 4L;
int >> long will promote to the larger data type (long) and it won't compile with resulted int type.
What have I received?
It compiles fine. Why?
The JLS won't "promote to the larger datatype" here, because it does not perform binary numeric promotion for shifting operators. This is covered by the JLS, Section 15.19.
Unary numeric promotion (§5.6.1) is performed on each operand separately. (Binary numeric promotion (§5.6.2) is not performed on the operands.)
Unary numeric promotion promotes the byte a to an int. The literal 4L is not changed, but it only needs to be a integral type anyway.
It is a compile-time error if the type of each of the operands of a shift operator, after unary numeric promotion, is not a primitive integral type.
Then for the shifting, only the least 5 significant bits are used to shift an int.
If the promoted type of the left-hand operand is int, then only the five lowest-order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & (§15.22.1) with the mask value 0x1f (0b11111). The shift distance actually used is therefore always in the range 0 to 31, inclusive.
The result of the operator is an int, not a long, so it can be assigned to an int without a compiler error.
The type of the shift expression is the promoted type of the left-hand operand.
By the JLS:
The type of the shift expression is the promoted type of the left-hand operand.
The right-hand operand of a shift operator doesn't have any effect on the expression's type. Unlike with an operator like +, a bigger type on the right doesn't mean the result could be any bigger or any smaller.
Related
byte a=10;
byte b=20;
b=a+b;
In this case, I need to explicitly convert a+b to byte like this :
b=(byte)(a+b);
It's the same with short :
short x=23;
short y=24;
Otherwise it gives an error.
But in case of integers, it's not required to convert explicitly :
int p=7788;
int q=7668;
p=p+q;
This will work just fine.
Why is that?
We don't need to explicitly cast even in the case of long as well.
If you look at the JLS 4.2.2 Integer Operations, it states that the result of a numerical operation between two integral operands is an int or a long. Since there's no implicit cast from an int to byte or a short, you need an explicit cast.
If you refer to JLS Sec 15.18.2, which deals with addition of numeric types, it says:
Binary numeric promotion is performed on the operands (§5.6.2).
...
The type of an additive expression on numeric operands is the promoted type of its operands.
JLS Sec 5.6.2 describes binary numeric promotion:
If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the following rules:
If either operand is of type double, the other is converted to double.
Otherwise, if either operand is of type float, the other is converted to float.
Otherwise, if either operand is of type long, the other is converted to long.
Otherwise, both operands are converted to type int.
So, in the case of int and long (where both operands are of that type), binary numeric promotion is a no-op: the operands remain int and long respectively, and the result of the addition is int and long respectively, meaning the result can be assigned to variables of that type.
In the case of byte and short, binary numeric promotion causes both of those to be widened to int to perform the addition, and the result of the addition is int; you have to explicitly cast back again to the narrower type, because not all int values fit into a byte or short.
There are 2 exceptions to this requirement to do an explicit narrowing cast.
Firstly, compound assignments: this would have worked:
b += a;
because, as stated in JLS Sec 15.26.2:
A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T) ((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.
In other words: the compiler inserts the cast for you:
b = (byte) ((b) + (a));
Secondly, if the operands have constant values, and the result of the addition is known to fit into the range of the narrower type, and you are doing the assignment at the variable declaration.
For example:
final byte a=10; // final is necessary for a and b to be constant expressions.
final byte b=20;
byte c = a + b;
This requires no cast.
With addition in java, java will promote the smaller data type to the larger one. And when the data type is smaller than int it will promote both the operant to int. Take a look at: Promotion in Java?
I'm preparing myself to a Java exam, and I'm reading "OCA Java SE 8 Programmer Study Guide (Exam 1Z0-808)". In operators section I found this sentence:
Shift Operators: A shift operator takes two operands whose type must be
convertible to an integer primitive.
I felt odd to me so I tested it with long:
public class HelloWorld{
public static void main(String []args){
long test = 3147483647L;
System.out.println(test << 1);
}
}
and it worked, no compiler errors and result is correct. Does the book has a bug or am I misunderstanding the quote from the book?
The shift operators >> and << are defined in JLS section 15.19. Quoting:
Unary numeric promotion (§5.6.1) is performed on each operand separately. (Binary numeric promotion (§5.6.2) is not performed on the operands.)
It is a compile-time error if the type of each of the operands of a shift operator, after unary numeric promotion, is not a primitive integral type.
When talking about "integer primitive", the book is really talking about "primitive integral type" (defined in JLS section 4.2.1):
The values of the integral types are integers in the following ranges:
For byte, from -128 to 127, inclusive
For short, from -32768 to 32767, inclusive
For int, from -2147483648 to 2147483647, inclusive
For long, from -9223372036854775808 to 9223372036854775807, inclusive
For char, from '\u0000' to '\uffff' inclusive, that is, from 0 to 65535
They're using integer not in the Java int fashion, but rather as "integer type instead of floating point or other type". Java's long is an integer too, it's just a 64-bit wide integer.
Why is the cast necessary here?
byte a = 0b0000_0000;
byte b = (byte) ~a;
a is a byte, b is a byte... why do you need to cast?
Because the Java Language Specification says so
Unary numeric promotion (§5.6.1) is performed on the operand. The type
of the unary bitwise complement expression is the promoted type of the
operand.
and
Otherwise, 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).
A value of type int is not assignable to a variable of type byte.
Java has no support for bit-wise operations on bytes. I would like to xor two bytes like so:
xoredByte = byte1 ^ byte2;
In java this would have to be done as:
xoredByte = (byte) (byte1 ^ byte2);
Which compiles to:
xoredByte = (byte) ((int)byte1 ^ (int)byte2);
Does this work in all cases? What I mean is, are these equivalent statements?
If not, what would the code be to perform this operation?
Yes, both statements are equivalent. When working with binary operators in general, including ^, Java applies "binary numeric promotion" to both operands to ensure that they are both at least int values. Section 5.6.2 of the JLS covers this:
When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order:
If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the following rules:
If either operand is of type double, the other is converted to double.
Otherwise, if either operand is of type float, the other is converted to float.
Otherwise, if either operand is of type long, the other is converted to long.
Otherwise, both operands are converted to type int.
(emphasis mine)
and
Binary numeric promotion is performed on the operands of certain operators:
The multiplicative operators *, /, and % (§15.17)
The addition and subtraction operators for numeric types + and - (§15.18.2)
The numerical comparison operators <, <=, >, and >= (§15.20.1)
The numerical equality operators == and != (§15.21.1)
The integer bitwise operators &, ^, and | (§15.22.1)
In certain cases, the conditional operator ? : (§15.25)
(emphasis mine)
Whether you apply the (int) casts or not, byte1 and byte2 will both be promoted to int before the operation. That is also why the cast back to (byte) is necessary.
What happens is that when the value is cast from byte to int, 24 new bits are inserted in front of the 8 bits of a byte. These bits could be all 0's or all 1's (since byte is a signed type). The result of ^ could therefore have 24 zeroes or 24 ones as the most significant bits. However, when the result is cast back to byte, those 24 bits are thrown away, no matter what they are; and since ^ is done on a bit-by-bit basis, the low-order 8 bits are the same as they would be if there were an ^ operation on bytes--the upper 24 bits don't have any effect on the lower 8.
quoting from oracle website "byte: The byte data type is an 8-bit signed two's complement integer. It has a minimum value of -128 and a maximum value of 127 (inclusive)".
here, the first two lines are valid but the last is not
byte b = -128;
byte b1 = 127;
byte b2 = b>>>b1;//illegal
Q1) what is meant exactly by 8-bit signed? 128 in binary format would be 1000 0000 and -128 would need an extra bit for the negative sign, where it would fit if all the 8 bits are occupied.
Q2) for int, there is a unsigned right shift operator, but that seems illegal with bytes, why is it so. can overflow not be prevented in case of bytes. In case of int, it works
Thanks for your help
Just what it sounds like: There are 8 bits, holding 2^8 = 256 possible values. It's signed, so the range is frmo -128 through 127 (256 values). The most significant bit has a value of -128.
In Java, binary numeric promotion occurs with operations such as b >>> b1. Both types are promoted to int, and the result is an int. However, you can explicitly cast the result back to byte.
Here's the cast:
byte b2 = (byte) (b >>> b1);
The JLS, Section 5.6.2, talks about binary numeric promotion:
Widening primitive conversion (§5.1.2) is applied to convert either or
both operands as specified by the following rules:
If either operand is of type double, the other is converted to double.
Otherwise, if either operand is of type float, the other is converted
to float.
Otherwise, if either operand is of type long, the other is converted
to long.
Otherwise, both operands are converted to type int.
(emphasis mine)