Type promotion Java - java

I`m learning Java with the Herbert Schildt book's: Java a Beginner's Guide.
In that book appears this code:
// A promotion surprise!
class PromDemo{
public static void main(String args[]){
byte b;
int i;
b = 10;
i = b * b; // OK, no cast needed
b = 10;
b = (byte) (b * b); // cast needed!!
System.out.println("i and b: " + i + " " + b);
}
}
I don't understand why I must use (byte) in the line:
b = (byte) (b * b); // cast needed!!
b was defined as a byte and the result of b * b is 100 which is right value for a byte (-128...127).
Thank you.

The JLS (5.6.2. Binary Numeric Promotion) gives rules about combining numeric types with a binary operator, such as the multiplication operator (*):
If either of the operands is of type double, the other one will be converted to a double.
Otherwise, if either of the operands is of type float, the other one will be converted to a float.
Otherwise, if either of the operands is of type long, the other one will be converted to a long.
Otherwise, both operands will be converted to an int.
The last point applies to your situation, the bytes are converted to ints and then multiplied.

In Java, byte and short will always be promoted to int, when you have a calculation like this:
byte b = 10;
b = (byte) (b * b);
So you actually multiply an integer with an integer, which will return an integer. Since you cannot assign an integer to a byte, you need the cast.
This is called "automatic type promotion" if you would like to Google it (to find e.g. https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2)

Related

Java prefix increment operator (++i) result type

JLS states:
If an integer operator other than a shift operator has at least one operand of type long, then the operation is carried out using 64-bit precision, and the result of the numerical operator is of type long. If the other operand is not long, it is first widened (§5.1.5) to type long by numeric promotion (§5.6).
Otherwise, the operation is carried out using 32-bit precision, and the result of the numerical operator is of type int. If either operand is not an int, it is first widened to type int by numeric promotion.
However when I try:
byte b = 18;
print(++b);
static void print(int a) {
System.out.println("Integer : " + a);
}
static void print(byte a) {
System.out.println("Byte : " + a);
}
The result is:
Byte : 19
I was expecting the method with integer type as input parameter to be called.
The other integer operators like unary plus or unary minus in fact return integer.
The ++ operator does not change the type.
The other operators do.
byte b = 1;
b = b + 1; // java: incompatible types: possible lossy conversion from int to byte

Cannot convert from to - Java's implicit type conversion and an unary operator

byte b1 = 3 + 5;
The literals 3, 5 and 3 + 5 are of type int.
The expression above works because Java internally converts the result of 3 + 5 from int to byte, or?
The same explanation should work in the case of b2:
byte b1 = 3 + 5;
byte b2 = b1 + 5; // Type mismatch: cannot convert from int to byte
But it does not.
Why?
The operands of + undergo binary numeric promotion: that is, they are widened where necessary to make them compatible for addition. The rule is quite simple:
If either operand is a double, widen the other to double
If either operand is a float, widen the other to float
If either operand is a long, widen the other to long
Otherwise, widen them both to int
in the second case, it is effectively (int) b1 + 5, the result of which is a non-compile-time constant int. This can't be guaranteed to be in the range of byte, so the compiler complains.
In the first case, both operands are compile-time constant ints, so the result is a compile-time constant int; the compiler can check that result is within the range of byte, and so can implicitly narrow the result.
ints are implicitly cast to bytes in your previous assignments, to mimic the effect in the last statement you need to do it explicitly after java promotes b1 + 5 to an int, like this:
byte b2 = (byte) b1 + 5;
alternatively if you declare b1 as final, the compiler will check the ranges and do the assignment (with an implicit cast) as you expected originally, since 3 + 5it is in the range of bytes and b1 will not be changing its value (3):
final byte b1 = 3;
byte b2 = b1 + 5;

Multiplying two bytes

Can somebody explain me why I can't to multiply two bytes in this way?
byte a = 1;
byte b = 1;
byte c = a*b;
or
byte a = 1;
byte b = 1;
short c = a*b;
Why I have to do that in this way?
byte a = 1;
byte b = 1;
byte c = (byte)(a*b);
or
byte a = 1;
byte b = 1;
int/double/float/long c = a*b;
When performing math with bytes, binary numeric promotion takes place, as specified by the JLS, Section 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.
(emphasis mine)
That forces you to assign to a type that is at least as wide as int or to cast back to byte.
Product of two bytes will not necessarily fit into a byte. So Java needs you to tell it that you know what you are doing and confirm that in case of overflow you will get the low-order bits.

Compilation error: casting

Can somebody explain me why the following piece of code fails to compile. The error is: "Possible loss of precision." :
byte a = 50;
byte b = 40;
byte sum = (byte)a + b;
System.out.println(sum);
Thank you.
Note that the cast has a higher precedence than the + operator. Your code does this:
byte a = 50;
byte b = 40;
byte sum = ((byte)a) + b;
System.out.println(sum);
The cast is redundant, since a is already a byte. You probably meant this:
byte a = 50;
byte b = 40;
byte sum = (byte) (a + b);
System.out.println(sum);
Because the two byte variables are operands to +, they are implicitly promoted to int. This is called Numeric Promotion. Because int is larger than byte, and the result of a + b yields int, casting to byte possibly chops off some bits, as int is larger than byte. Hence the "loss of precision"
Doc for implicit numeric promotion:
http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#170983
Doc for the size of types:
http://java.sun.com/docs/books/tutorial/java/nutsandbolts/datatypes.html
You did right by identifying that a cast is required, but unfortunately you did not apply it to the right expression due to precedence of operators.
Consider the following snippet:
static void f(char ch) {
System.out.println("f(char)");
}
static void f(int i) {
System.out.println("f(int)");
}
public static void main(String[] args) {
char ch = 'X';
f( (char) ch + 1 ); // prints "f(int)"
f( (char) (ch + 1) ); // prints "f(char)"
}
The cast has a higher precedence than the addition, which is why the snippet prints what it does. That is, the first call is equivalent to f( ((char) ch) + 1 );. The result of the addition is an int, which is why the f(int) overload is invoked.
The lesson here is that you should always use parenthesis unless you're doing a very simple cast. In general, always consider using parentheses to make the order of evaluation explicit, even if they're not necessary. They lead to a better, more readable code.
Bytes are added using "int" arithmetic; thus the result is an int and must be cast back from int to byte, which leads to the possibility of truncation.

Promotion in Java?

The rules for promotion is "when operands are of different types, automatic binary numeric promotion occurs with the smaller operand type being converted to the larger". But the operands are of same type for example,
byte=byte+byte // Compile time error... found int..
So why is it so?
There's no + operator for byte. Instead, both operands are promoted to int, so you've got
byte = byte + byte
... becomes (widening to find + operator) ...
byte = int + int
... becomes (result of + operator) ...
byte = int
... which then fails because there's no implicit conversion from int to byte. You need to cast:
byte a = 1;
byte b = 2;
byte c = (byte) (a + b);
Here are the actual rules for numeric promotion, from section 5.6.2 of the JLS:
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.
You were provided with correct answer about automatic promotion to 'int'.
There is one more note about that - compound assignment operators behave as they have an implicit type case. Example:
byte b1 = 1;
byte b2 = 2;
b1 = b1 + b2; // compilation fails
b1 += b2; // compilation successful
I would like to talk about Promotion in general
Java can evaluate only arithmetic expressions in which the operands’ types are identical
For example, in an expression containing int and double values, the int values are promoted to double values for use in the expression.
in another word
double someVar = 1 / 2;// someVar = 0
but
double someVar = (double)1 / 2;// someVar = 0.5
why?
we use the (double) cast operator to create a temporary
floating-point copy of its operand "1" (it's called explicit conversion)
The calculation now consists of a floating-point value (the temporary double copy of 1) divided by the integer 2
according to the above statement, Java performs an operation called promotion (or implicit conversion), so the int values are promoted to double values for use in the expression => the integer 2 is promoted to double
the expression became double someVar = 1.0 / 2.0; // someVar= 0.5
hope this is helpful, even if it is out of the core of the question
To understand this you should refer the 2 things:
Implicit Casting:
byte (8 bit) -> short (16 bit) -> int (32 bit) -> float (32 bit) -> double (64 bit)
char (16 bit) -> int (32 bit)
Arithmetic Operator Rules
Operator Precedence Rule : This rule states that Group of (*,/, %) will be evaluated first. Then Group of (+,-) operator will be evaluated. From a same Group of Operators, calculate from the left.
Operand Promotion Rule : This rule states that Operands having data type smaller than int will be promoted to int. order of promotion (byte->short->int, char->int)
Same Type Operand Rule: This rule states that if both operands are int,long,float,double then the same type is carried to the result type.
i.e. long+long => long , float + float => float, int+int => int
Mix Type Operand Rule : Follow the order of Promotion (int->long->float->double) if any of the operand is from the above order then the smaller will be promoted to the bigger one and result will be calculated in bigger type.
i.e. long + double => double , int + long => long
From this SO question, and above answers due to + arithmetic operator, both operands are converted to type int.
byte b1 = 1;
byte b2 = 2;
byte b3 = b1 + b2; // compile time error
In above code, value of b1 and b2 will be resolved at runtime, so compiler will convert both to int before resolving the value.
But if we consider the following code,
final byte b1 = 1;
final byte b2 = 2;
int b3 = b1 + b2; // constant expression, value resolved at compile time
b1 and b2 are final variables and values will be resolved at compile time so compilation won't fail.

Categories