The JLS says that
The type of the shift expression is the promoted type of the left-hand
operand.
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.
If the promoted type of the left-hand operand is long, then only the
six 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 0x3f
(0b111111). The shift distance actually used is therefore always in
the range 0 to 63, inclusive.
So if I explicitly make a byte and a short operand using the cast operator like (byte)100<<100 and (short)100<<100, what will be the usable bits of the right operand?
Edit: Does an operand undergo a numeric promotion (unary/binary) if it has already been converted to a different (smaller) type using a casting operator? If this is the case, how would you explain the expression having byte variables b1 = (byte)(b2 + b3) because after the cast conversion, the byte result will probably convert to int as per the numeric promotion?
The Java 8 JLS also states in §5.6.1:
5.6.1. Unary Numeric Promotion
Some operators apply unary numeric promotion to a single operand, which must produce a value of a numeric type:
...
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).
...
Thus, if we take the following expression:
int i = ...
short s = (short) i << 2;
Will result in a compiler error:
Main.java:4: error: incompatible types: possible lossy conversion from int to short
short s = (short) i << 2;
Ideone demo
This is due to the fact that the cast binds to the first argument of the shift, not the whole expression. Here is the whole expression with explicit parenthesis:
short s = ((byte) i) << 2;
Whereas
int i = ...;
int j = (short) i << 2;
Will successfully compile.
Ideone demo
So the effective bits to use for anything < int is the same as for int (5 bits) since they are upcasted to int automatically.
If you cast the result of the whole expression to, e.g. short (short s = (short) (i << 2), then there is no automatism that takes place in the compiler. But Bohemian's answer gives a logical bound as to what bits of the right hand operator will effectively influence the value after the cast.
In newer JLS versions, the section has been reworded. For example, in Java 14 JLS, §5.6 we find (the section is shortened for breviety, I recommend reading the whole paragraph to get the full context):
5.6. Numeric Contexts
Numeric contexts apply to the operands of arithmetic operators, array creation and access expressions, conditional expressions, and the result expressions of switch expressions.
An expression appears in a numeric arithmetic context if the expression is one of the following:
...
An operand of a shift operator <<, >>, or >>> (§15.19). Operands of these shift operators are treated separately rather than as a group. A long shift distance (right operand) does not promote the value being shifted (left operand) to long.
...
Numeric promotion determines the promoted type of all the expressions in a numeric context. The promoted type is chosen such that each expression can be converted to the promoted type, and, in the case of an arithmetic operation, the operation is defined for values of the promoted type. The order of expressions in a numeric context is not significant for numeric promotion. The rules are as follows:
...
Next, widening primitive conversion (§5.1.2) and narrowing primitive conversion (§5.1.3) are applied to some expressions, according to the following rules:
...
Otherwise, none of the expressions are of type double, float, or long. In this case, the kind of context determines how the promoted type is chosen.
In a numeric arithmetic context or a numeric array context, the promoted type is int, and any expressions that are not of type int undergo widening primitive conversion to int.
In a numeric choice context, the following rules apply:
...
Otherwise, the promoted type is int, and all the expressions that are not of type int undergo widening primitive conversion to int.
...
The number of bits of the maximum usable shift distance is given by log2n, where n is the number of bits used to represent values of the left hand type.
byte has 8 bits. log28 is 3. So only the right most 3 bits are used, giving a shift value in the range 0-7.
short has 16 bits. log216 is 4. So only the right most 4 bits are used, giving a shift value in the range 0-15.
Related
In Java when I print something like this
System.out.println(1.0f*1l));
I get an output of
1.0
or with
System.out.println(((byte)1)*'A');
I get an output of
65
In both cases, one of the types is bigger than the other. Longs are 64-bits while floats are 32-bits, and bytes are 8-bits while chars are 16-bits. Despite this, Java outputs the result in the smaller type. Wouldn't this be considered a loss of precision?
Java Language Specification provides a list of rules governing the type of result based on the types of operands.
In particular, section 4.2.4 says that
If at least one of the operands to a binary operator is of floating-point type, then the operation is a floating-point operation, even if the other is integral.
which explains why float "wins" against a long.
Integer arithmetic is explained in section 5.6.2. Specifically, it says that
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.
That is why in your second example the result, 65, is of type int.
Java widens type based on the range of possible values. This means float is considered wider thanlong.
Also integer operations which are not long are performed as int.
A byte * char is an int
You are asking about Binary Numeric Promotion (JLS 5.6.2) (emphasis mine):
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.
The expression 1.0f*1l is a float and long, so the long is converted to a float.
The expression ((byte)1)*'A' is a byte and a char, so they are both converted to int.
In addition to the other answers that spell out the rules in detail, I think it's also worth considering why it should be this way. Suppose you do
2.7F * 2L
The true mathematical answer to this multiplication is 5.4. Therefore if the answer were a long, it would have to be 5L (5 is the closest integer to 5.4). Because of this kind of thing it makes much more sense for the result of multiplying a floating-point value by an integer to be a floating point value. I think there would be many more questions about this on Stack Overflow if the following line of code printed 5.
System.out.println(2.7F * 2L);
In fact it prints 5.4.
It also makes sense that all operations between integer types (except long) are done with int. After all, a byte is usually thought of as a small packet of data, and a char is usually thought of as a character. From these perspectives multiplying byte or char values isn't really meaningful. It makes much more sense to keep the rules simple and just do the operations with int.
This question already has an answer here:
Why byte and short values are promoted to int when an expression is evaluated [duplicate]
(1 answer)
Closed 7 years ago.
When we do addition,subtractions,divide(etc..)in byte short data types the result is coming from int value...why is that...?
e.g:- in above code does not compile because the result in c is coming from int why is that happen...??? when we try to print c using system out it came error because result is in int
public class A {
public static void main(String[] args) {
byte a = 12;
byte b = 10;
byte c = a + b;
System.out.println(c);
}
}
According to java doc the when we perform operations on two bytes there is probability that the output would be a number which can't be handled by byte, so it was decided by creators of java to auto cast it to Integer. So it's like this.
This is well answered here, starting from an identical scenario :) http://www.coderanch.com/t/499127/java/java/Adding-bytes
To quote http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.6.2
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, using widening conversion (§5.1.2) to convert operands as necessary:
If any of the operands is of a reference type, unboxing conversion (§5.1.8) is performed. Then:
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.
The spec says:
https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.18.2
The binary + operator performs addition when applied to two operands of numeric type, producing the sum of the operands.
The binary - operator performs subtraction, producing the difference of two numeric operands.
Binary numeric promotion is performed on the operands (§5.6.2).
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
The type of an additive expression on numeric operands is the promoted type of its operands.
https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2
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.
If you combine the two you see that any operand in an addition will be promoted to either int or long and any primitive smaller than int will be promoted to int. The result of the operation is the type of both operands - which it int.
The reasoning behind that is most probably that int is the most common datatype which reduces probability of overflow while not requiring as much space as a long. The space argument probably isn't as generally valid as it used to be anymore but that part of the spec was defined like in 1995 and an inherent part of the language (which you can't change without breaking compatibility).
Any Binary arithmetic operations we performed on char & byte datatype(and short) promote it to int.
For more details please refer JLS 5.6.2.
This works:
final byte a = 12;
final byte b = 10;
byte c = a + b;
Or this:
byte a = 12;
byte b = 10;
byte c = (byte)( a + b);
In Java, the following is allowed:
char c = 'A' + 1;
Here, c will hold the value 'B'. Above, first the expression is evaluated. So 'A' gets converted to 65, the whole expression evaluates to 66, and then 66 is converted to 'B' since we are storing the value in a char.
The following, however, gives a compile-time error:
char c = 'A';
c = c + 1;
What is the explanation for how Java views the expressions differently? By the way, the following works fine too:
char c = 'A';
c++;
The first example (which compiles) is special because both operands of the addition are literals.
A few definitions to start with:
Converting an int to char is called a narrowing primitive conversion, because char is a smaller type than int.
'A' + 1 is a constant expression. A constant expression is (basically) an expression whose result is always the same and can be determined at compile-time. In particular, 'A' + 1 is a constant expression because the operands of + are both literals.
A narrowing conversion is allowed during the assignments of byte, short and char, if the right-hand side of the assignment is a constant expression:
In addition, if the expression [on the right-hand side] is a constant expression of type byte, short, char, or int:
A narrowing primitive conversion may be used if the variable is of type byte, short, or char, and the value of the constant expression is representable in the type of the variable.
c + 1 is not a constant expression, because c is a non-final variable, so a compile-time error occurs for the assignment. From looking at the code, we can determine that the result is always the same, but the compiler isn't allowed to do that in this case.
One interesting thing we can do is this:
final char a = 'a';
char b = a + 1;
In that case a + 1 is a constant expression, because a is a final variable which is initialized with a constant expression.
The caveat "if […] the value […] is representable in the type of the variable" means that the following would not compile:
char c = 'A' + 99999;
The value of 'A' + 99999 (which is 100064, or 0x186E0) is too big to fit in to a char, because char is an unsigned 16-bit integer.
As for the postfix ++ operator:
The type of the postfix increment expression is the type of the variable.
...
Before the addition, binary numeric promotion* is performed on the value 1 and the value of the variable. If necessary, the sum is narrowed by a narrowing primitive conversion and/or subjected to boxing conversion to the type of the variable before it is stored.
(* Binary numeric promotion takes byte, short and char operands of operators such as + and converts them to int or some other larger type. Java doesn't do arithmetic on integral types smaller than int.)
In other words, the statement c++; is mostly equivalent to:
c = (char)(c + 1);
(The difference is that the result of the expression c++, if we assigned it to something, is the value of c before the increment.)
The other increments and decrements have very similar specifications.
Compound assignment operators such as += automatically perform narrowing conversion as well, so expressions such as c += 1 (or even c += 3.14) are also allowed.
char to int conversion is called widening conversions. In widening conversions, values do not lose information about the overall magnitude of a numeric value where as int to char conversion is called narrowing conversions. With narrowing conversion you may lose information about the overall magnitude of a numeric value and may also lose precision.
For more information on primitive conversions refer this document.
It is because the compiler can check that it ('A' + 1) is within the bounds of a char whereas it cannot (in general) check that c + <an integer> is within the bounds.
Its because the literals for integer or smaller than int as byte ,short and char is int. Understand the following in this way.
code:
byte a = 10;//compile fine
byte b= 11;//compile fine
byte c = a+b;//compiler error[says that result of **a+b** is **int**]
the same happens for any mathematical operations as of 'Divide', 'multiply', and other arithmetic operation. so cast the result to get the literal in desired data type
byte c = (byte)(a+b);
so when you perform
c= c+1;//compiler error
Its the result of c+1 is int not a char . so compiler give a compile time error for the same.
so you need to provide a primitive cast to change the literal to char data type.
Hope this example provide some understanding..
This loop will continue indefinitely:
char a = 100;
for(a=100; a>=0;--a)
System.out.println(a);
Does it happen because a gets promoted to an int value for arithmetic operations and gets widened to 32 bits from 16 bit char value and hence will always stay positive?
It will indeed loop indefinitely -- and the reason you stated is close. It happens because a can't represent any number that doesn't satisfy a >= 0 -- char is unsigned. Arithmetic underflow is well-defined in Java and unindicated. See the below relevant parts of the specification.
§4.2.2
The integer operators do not indicate overflow or underflow in any way.
This means there is no indication of overflow/underflow other than just comparing the values... e.g. if a <= --a, then that means an underflow occured.
§15.15.2
Before the subtraction, binary numeric promotion (§5.6.2) is performed on the value 1 and the value of the variable. If necessary, the difference is narrowed by a narrowing primitive conversion (§5.1.3) and/or subjected to boxing conversion (§5.1.7) to the type of the variable before it is stored. The value of the prefix decrement expression is the value of the variable after the new value is stored.
So, we can see that there are two major steps here: binary numeric promotion, followed by a narrowing pritimive conversion.
§5.6.2
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.
We can see that the decrement expression works with a treated as int, thus performing a widening conversion. This allows it to represent the value -1.
§5.1.3
A narrowing primitive conversion may lose information about the overall magnitude of a numeric value and may also lose precision and range.
...
A narrowing conversion of a signed integer to an integral type T simply discards all but the n lowest order bits, where n is the number of bits used to represent type T. In addition to a possible loss of information about the magnitude of the numeric value, this may cause the sign of the resulting value to differ from the sign of the input value.
Keeping only the n lowest order bits means that only the lowest 16 bits of the int expression a - 1 are kept. Since -1 is 0b11111111 11111111 11111111 11111111 here, only the lower 0b11111111 11111111 is saved. Since char is unsigned, all of these bits contribute to the result, giving 65535.
Noticing something here? Essentially, this all means that Java integer arithmetic is modular; in this case, the modulus is 2^16, or 65536, because char is a 16-bit datatype. -1 (mod 65536) ≡ 65535, so the decrement will wrap back around.
Nope. char values are unsigned - when they go below 0, they come back around to 65535.
Swap char with byte - then it'll work.
As others have said, the char type in Java is unsigned, so a >= 0 will always be true. When a hits 0 and then is decremented once more, it becomes 65535. If you just want to be perverse, you can write your loop to terminate after 101 iterations this way:
char a = 100;
for(a=100; a<=100;--a)
System.out.println(a);
Code reviewers can then have a field day tearing you apart for writing such a horrible thing. :)
As i understand, when you cast between 2 of these types with an arithmetic operation in Java, for example double + int, the result will be as the bigger type (meaning in this example, the result will be double). What happens when you make an arithmetic operation on 2 types with the same size? what will int + float and long + double give? since int and float are 4 bytes each, and long and double are 8 bytes.
This is all specified by the binary numeric promotion rules in the JLS. From http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2:
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.
After the type conversion, if any, value set conversion (§5.1.13) is
applied to each operand.
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)
("value set conversion" is about mapping between floating-point representations.)
int + float will give you float (note that you'll have to cast result to float because double is used by default). long + double will give you double.
Still going to return the "bigger" type that can hold the entirely value. In your specific question
if you make a + between a int and a float the return will be a float
and long + double returns a double,
The behavior of the additive operators + and - is defined in by 15.18.2. Additive Operators (+ and -) for Numeric Types of the JLS. It states that it first performs a binary numeric promotion:
Binary numeric promotion is performed on the operand.
This in turns is defined by 5.6.2. Binary Numeric Promotion. In substance, for primitives:
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.
There are two an interesting FAQ on type conversion that can be found in: http://www.programmersheaven.com/2/FAQ-JAVA-Type-Conversion-Casting
http://myhowto.org/java/60-understanding-the-primitive-numeric-type-conversions-in-java/
Answering your questions on two types of the same size, the return value is of the type with the biggest precision.
Try the following code:
public static void main(String[] args) {
int i=1;
float f=2.5f;
long l=10;
double d=3.74;
System.out.println(i+f);
System.out.println(f+i);
System.out.println(l+d);
System.out.println(d+l);
}
You will see that the results are 3.5 and 13.74, which are a float and a double, respectively (tested in Netbeans 6.9 and java 1.6).
A gotcha of this "promotion" is that a long + float will "widen" to using a float.
e.g.
System.out.println(1111111111111111111L + 0.0f);
System.out.println(1111111111111111111L + 0.0);
prints
1.11111113E18
1.11111111111111117E18
When dealing with long and float, you may not get a wider type and can lose more precision than you might expect.