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);
Related
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.
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.
Can you please run the below and explain?
Object o = true ? new Integer(1) : new Double(2.0);
System.out.println(o);
I found that surprising as someone would expect 1 to be printed and not 1.0
It's not a surprise at all, although it might seem like one. The behaviour is specified in JLS §15.25 - Conditional Operator:
Otherwise, if the second and third operands have types that are
convertible (§5.1.8) to numeric types, then there are several cases:
If one of the operands is of type byte or Byte and the other is of
type short or Short, then the type of the conditional expression is
short.
[...]
Otherwise, binary numeric promotion (§5.6.2) is applied to the operand
types, and the type of the conditional expression is the promoted type
of the second and third operands.
Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).
So the Integer and Double types are unboxed to their respective primitive counterparts - int and double, as a process of binary numeric promotion. And then the type of the conditional operator is the promoted type of int and double, which is double. Hence the result is 1.0. And of course the final result is then boxed back to Double.
Here is an article published in DZone yesterday talking about it:
Java auto unboxing gotcha
Funny enough, the example code looks similar...
I'm making a small Java program which encrypts any type of file. The way I'm doing it, is the following: I open the input file, read it in a byte array with the same size as that file, then do the encoding, and write the whole array to a .dat file called output.dat. To index the byte array, I'm using a variable of type int. The code:
for(int i : arr) {
if(i>0) {
arr[i] = arr[i-1]^arr[i];
}
}
'arr' is a byte array with the same size as the input file.
The error I get: CodingEvent.java:42: error: possible loss of precision
arr[i] = arr[i-1]^arr[i];
(an arrow spots on the ^ operator)
required: byte
found: int
What's wrong? Could you help me please?
The result of byte ^ byte is, counter-intuitively, int. Use a cast on the result of the expression when assigning it back to arr[i]:
arr[i] = (byte)(arr[i-1]^arr[i]);
This is because the operator is defined as doing a binary numeric promotion on its operands, and so what it's really doing (in this case) is:
arr[i] = (int)arr[i-1]^(int)arr[i];
...which naturally results in int. Which is why we need the cast back.
The operands of the ^ operators are first converted to an int (it is called binary numeric promotion). So both bytes (arr[i-1] and arr[i]) are converted to an int and the result of the operation is an int too.
You need to cast the result back to a byte to assign it to arr[i].
If arr[] is of type byte[] then that's the problem, when java does any binary operation with integers, it returns wither an int or long depending of the operators. In this case the result of arr[i-1]^arr[i] is an int that you're trying to store in a byte.
Look at the JLS 15.22.1
When both operands of an operator &, ^, or | are of a type that is convertible (§5.1.8) to a primitive integral type, binary numeric promotion is first performed on the operands (§5.6.2).
And JLS 5.6.2
1.If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
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.
Hence the below expression is first converted to an int .
arr[i-1]^arr[i];
To cast it back to byte , use an explicit cast :
arr[i] = (byte)(arr[i-1]^arr[i]);
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.