recently I learned about the two's compliment method of representing both positive and negative integers in the base two system. I then tried to see this in action using java with the following short code:
int a=2147483647;
System.out.println("a: "+a);
System.out.println("a+1: "+(a+1));
short b=32767;
System.out.println("b: "+b);
System.out.println("b+1: "+(b+1));
Which outputs:
a: 2147483647
a+1: -2147483648
b: 32767
b+1: 32768
Which confuses me because I would think that b+1, being represented in binary as 011111111111111, would turn into 1000000000000000, or in decimal, -32768. What's going on?
Although b is a short, the expression (b + 1) is an int. The right operand is an int, the left operand is promoted to an int, and the expression is the promoted type of the operands.
From the Java Language Specification, 5.6.2. 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.
Note that this last promotion occurs even if both operands are of short type. You can't avoid promotion to an int with (b + (short) 1).
From 15.18.2. Additive Operators (+ and -) for Numeric Types
The type of an additive expression on numeric operands is the promoted type of its operands.
No need for confusion, try this:
short b = 32767;
short c = b + 1;
What do you get? Right, you get:
error: incompatible types: possible lossy conversion from int to short
short c = b + 1;
So, what happens at your line?
System.out.println("b+1: "+(b+1));?
Well, b+1 is too big for short, as you said correctly, but here you add an int to it, making the result an int as well. And 32768 is a valid int.
As others already pointed out, if you explicitly cast it down to (short) you get what you asked for.
On the other hand that does not work for short c = b + 1; as here the declared type is a short, while the actual type is an int.
short c = (short) (b + 1); solves that "problem"
doing + automatically promotes short to int. Do this and you will see the overflow.
System.out.println("b+1: "+(short)(b+1)); //-32768
From the Java Language Specification, 5.6.2. 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.
Notice the last rule, so essentially that means that even if they are both shorts, they will be promoted to int, this can't be avoided.
you could:
short b= 32767;
short d= 12;
System.out.println("b+1: "+ (d+b)); // 32779
And the answer would still be valid.
1 is an int literal. When you compute b+1, you in fact promote b to an int and then add 1, resulting in 32768, which is a perfectly legal int value. If you cast it down to a short again, you'll see the overflow you're expecting (-32768):
System.out.println("b+1: " + (short)(b + 1));
As others have noted, addition promotes the operands to int.
Note that the += operator automatically does the casting back to short:
short b=32767;
System.out.println(b + 1); // 32768, because of integer promotion.
b += 1; // Equivalent to b = (short)(b + 1);
System.out.println(b); // -32768
b++;/++b; would yield the same result as b += 1.
Related
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.
I have seen it discussed somewhere that the following code results in obj being a Double, but that it prints 200.0 from the left hand side.
Object obj = true ? new Integer(200) : new Double(0.0);
System.out.println(obj);
Result: 200.0
However, if you put a different object in the right hand side, e.g. BigDecimal, the type of obj is Integer as it should be.
Object obj = true ? new Integer(200) : new BigDecimal(0.0);
System.out.println(obj);
Result: 200
I presume that the reason for this is something to do with casting the left hand side to a double in the same way that it happens for integer/double comparisons and calculations, but here the left and right sides do not interact in this way.
Why does this happen?
You need to read section 15.25 of the Java Language Specification.
In particular:
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.
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representable in type T, then > - the type of the conditional expression is T.
If one of the operands is of type Byte and the other operand is a constant expression of type int whose value is representable in type byte, then the type of the conditional expression is byte.
If one of the operands is of type Short and the other operand is a constant expression of type int whose value is representable in type short, then the type of the conditional expression is short.
If one of the operands is of type; Character and the other operand is a constant expression of type int whose value is representable in type char, then the type of the conditional expression is char.
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 unboxing conversion (§5.1.8) and value set conversion (§5.1.13).
So binary numeric promotion is applied, which starts with:
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.
That's exactly what happens here - the parameter types are converted to int and double respectively, the second operand (the third in the original expression) is then of type double, so the overall result type is double.
Numeric conversion in the conditional operator ? :
In the conditional operator a?b:c, if both b and c are different numeric types, the following conversion rules are applied at compile time to make their types equal, in order:
The types are converted to their corresponding primitive ones, which is called unboxing.
If one operand were a constant int (not Integer before unboxing) whose value is representable in the other type, the int operand is converted into the other type.
Otherwise the smaller type is converted into the next greater one until both operands have the same type. The conversion orders are:
byte -> short -> int -> long -> float -> double
char -> int -> long -> float -> double
Eventually the whole conditional expression gets the type of its second and third operands.
Examples:
If you combine char with short, the expression becomes int.
If you combine Integer with Integer, the expression becomes Integer.
If you combine final int i = 5 with a Character, the expression becomes char.
If you combine short with float, the expression becomes float.
In the question's example, 200 is converted from Integer into double, 0.0 is unboxed from Double into double and the whole conditional expression becomes becomes double which is eventually boxed into Double because obj is of type Object.
Example:
public static void main(String[] args) {
int i = 10;
int i2 = 10;
long l = 100;
byte b = 10;
char c = 'A';
Long result;
// combine int with int result is int compiler error
// result = true ? i : i2; // combine int with int, the expression becomes int
//result = true ? b : c; // combine byte with char, the expression becomes int
//combine int with long result will be long
result = true ? l : i; // success - > combine long with int, the expression becomes long
result = true ? i : l; // success - > combine int with long, the expression becomes long
result = true ? b : l; // success - > combine byte with long, the expression becomes long
result = true ? c : l; // success - > char long with long, the expression becomes long
Integer intResult;
intResult = true ? b : c; // combine char with byte, the expression becomes int.
// intResult = true ? l : c; // fail combine long with char, the expression becomes long.
}
Can you please explain what's going in the last 2 print statements? That's where I get lost.
public class Something
{
public static void main(String[] args){
char whatever = '\u0041';
System.out.println( '\u0041'); //prints A as expected
System.out.println(++whatever); //prints B as expected
System.out.println('\u0041' + 1); //prints 66 I understand the unicode of 1 adds up the
//unicode representing 66 but why am I even returning an integer when in the previous statement I returned a char?
System.out.println('\u0041' + 'A'); //prints 130 I just wanted to show that adding an
//integer to the unicode in the previous print statement is not implicit casting because
//here I add a char which does not implicitly cast char on the returned value
}
}
This happens because of Binary Numeric Promotion
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.
Basically, both operands are converted to an int, and then the System.out.println(int foo) is called. The only types that can be returned by +, *, etc. are double, float, long, and int
'\u0041' + 1 produces int, you need to cast it to char so that javac binds the call to println(char) instead of prinln(int)
System.out.println((char)('\u0041' + 1));
whatever is a char, and ++whatever means whatever = whatever + 1 (ignoring prefix order)
since there is an assignment involved, result is converted to char, so the char method is called. But in 3-4th print, there is no assignment, and as per rule, all the sum operation are by default happens in int. So before print operation, it sums up the char + char and char+int, and since there is no back assignment, it remains as int after the opration, so the integer method is called.
I have seen it discussed somewhere that the following code results in obj being a Double, but that it prints 200.0 from the left hand side.
Object obj = true ? new Integer(200) : new Double(0.0);
System.out.println(obj);
Result: 200.0
However, if you put a different object in the right hand side, e.g. BigDecimal, the type of obj is Integer as it should be.
Object obj = true ? new Integer(200) : new BigDecimal(0.0);
System.out.println(obj);
Result: 200
I presume that the reason for this is something to do with casting the left hand side to a double in the same way that it happens for integer/double comparisons and calculations, but here the left and right sides do not interact in this way.
Why does this happen?
You need to read section 15.25 of the Java Language Specification.
In particular:
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.
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representable in type T, then > - the type of the conditional expression is T.
If one of the operands is of type Byte and the other operand is a constant expression of type int whose value is representable in type byte, then the type of the conditional expression is byte.
If one of the operands is of type Short and the other operand is a constant expression of type int whose value is representable in type short, then the type of the conditional expression is short.
If one of the operands is of type; Character and the other operand is a constant expression of type int whose value is representable in type char, then the type of the conditional expression is char.
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 unboxing conversion (§5.1.8) and value set conversion (§5.1.13).
So binary numeric promotion is applied, which starts with:
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.
That's exactly what happens here - the parameter types are converted to int and double respectively, the second operand (the third in the original expression) is then of type double, so the overall result type is double.
Numeric conversion in the conditional operator ? :
In the conditional operator a?b:c, if both b and c are different numeric types, the following conversion rules are applied at compile time to make their types equal, in order:
The types are converted to their corresponding primitive ones, which is called unboxing.
If one operand were a constant int (not Integer before unboxing) whose value is representable in the other type, the int operand is converted into the other type.
Otherwise the smaller type is converted into the next greater one until both operands have the same type. The conversion orders are:
byte -> short -> int -> long -> float -> double
char -> int -> long -> float -> double
Eventually the whole conditional expression gets the type of its second and third operands.
Examples:
If you combine char with short, the expression becomes int.
If you combine Integer with Integer, the expression becomes Integer.
If you combine final int i = 5 with a Character, the expression becomes char.
If you combine short with float, the expression becomes float.
In the question's example, 200 is converted from Integer into double, 0.0 is unboxed from Double into double and the whole conditional expression becomes becomes double which is eventually boxed into Double because obj is of type Object.
Example:
public static void main(String[] args) {
int i = 10;
int i2 = 10;
long l = 100;
byte b = 10;
char c = 'A';
Long result;
// combine int with int result is int compiler error
// result = true ? i : i2; // combine int with int, the expression becomes int
//result = true ? b : c; // combine byte with char, the expression becomes int
//combine int with long result will be long
result = true ? l : i; // success - > combine long with int, the expression becomes long
result = true ? i : l; // success - > combine int with long, the expression becomes long
result = true ? b : l; // success - > combine byte with long, the expression becomes long
result = true ? c : l; // success - > char long with long, the expression becomes long
Integer intResult;
intResult = true ? b : c; // combine char with byte, the expression becomes int.
// intResult = true ? l : c; // fail combine long with char, the expression becomes long.
}
I'm wondering why there is an inconsistency with the code below. I would expect the same output, but when using the inline conditional statement, it appends a .0 to the string.
Do I have some error in my code?
double d = 10.1;
String rounded = (false ? d : Math.round(d)) + "";
System.out.println(rounded);//10.0
rounded = Math.round(d) + "";
System.out.println(rounded);//10
Math.round returns a long, therefore the two operands of the conditional operator do not have the same type, and thus a more complex rule is followed to determine the type of the overall operation, as defined in JLS §15.25:
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 unboxing conversion (§5.1.8) and value set conversion
(§5.1.13).
And from 5.6.2, binary numeric promotion:
If either operand is of type double, the other is converted to double.
And to illustrate the pitfalls with the conditional operator and for some fun, from Java puzzlers (puzzle 8):
char x = 'X';
int i = 0;
System.out.print(true ? x : 0); // prints X
System.out.print(false ? i : x); // prints 88 => (int)X
Also, check out the Hamlet and Elvis examples (video links).
The type returned from a ternary operator may be promoted so that the two potential return types match. This is called binary numeric promotion, and your variable is being promoted from long to double before conversion to a String.
If you had this where both potential return types are int or long:
double d = 10.1;
String rounded = (false ? 0 : Math.round(d)) + "";
What happens (not a rhetorical question since I'm no where near a Java compiler)?