Thursday, 28 May 2015

Java Type Conversions during Assignments

Types of the left hand side variable and right hand side expression in an assignment may differ from each other as long as they are type-compatible with each other. For example all the primitive types excluding boolean are type-compatible with each other. When value of an expression is assigned to a variable, it should be converted to the type of the variable on the left hand side. The type conversion may take place automatically or we may have to convert the type explicitly using typecast operator.

Type conversions can be classified as:

  • Widening (or Broadening or UpCasting) Type Conversions
  • Narrowing (or DownCasting) Type Conversions
Widening Conversions

Widening conversion takes place when we assign a lower type expression to a higher type. There is no possibility of any data/precision loss in a widening conversion so it takes place implicitly/automatically.

Examples: The following examples will clarify this.

Suppose following variables are declared in a method/block:

int i ; byte b= 3; short s = 16; char c = 65; long l; float f; double d;

The following expressions are valid as widening conversion takes place automatically:

i  = b; l = s; f = b; d = c;

Narrowing Conversions


Narrowing conversion takes place when we assign a higher type expression to a lower type.

There is possibility of data/precision loss in a narrowing conversion. So normally a narrowing conversion does not take place implicitly/automatically. The programmer needs to explicitly typecast the higher type to lower type so that he/she is aware of the possibility of data/precision loss. However, there are some cases where even narrowing conversion takes place automatically.

Simple assignments always need explicit type conversion.

Examples: The following examples will clarify this.

Suppose following variables are declared in a method/block:

int i = 10 ; byte b; short s; char c; long l = 23; float f = 18.1f; double d = 5.77;

The following expressions are not valid, as narrowing conversions need explicit typecast:

b = i; s  = l; b = f; c = d;

To use these expressions you need to do explicit typecasting as follows:

b = (byte) i; s = (short) l; b = (byte) f; c = (char) d;

Java Arithmetic Expressions

Arithmetic expressions are formed by combining numeric types (constants, variables and expressions) using arithmetic operators. The basic concepts are same as in case of C/C++ although there are some differences.

1. Type Conversions in Expressions

Automatic type promotion takes place in arithmetic expressions according to the following rules:

  • An operand of type byte, short or char is always converted to int. This is true even if unary operator is used.  This implies that the type of the result will always be int or some higher type.
  • If the higher type between two operands is long the other is converted to long.
  • If the higher type between two operands is float the other is converted to float.
  • If the higher type between two operands is double the other is converted to double.
  • If both the operands are of same type (after byte, short  or char has been promoted to int) then no conversion takes place.
Examples: The following examples will clarify these rules.

Suppose following variables are declared in a method/block:

int i = 10; byte b= 3; short s = 16; char c = 65; long l = 100;

float f = 10.3f; double d = 4.88;

The following table demonstrates some expressions formed using above variables, their result type and the value calculated using the conversion rules defined above:

2. Integer Expressions

An Integer expression is one where all the operands are of integer type. The result of such an expression will always be integer as in case of C/C++. The fractional part is simply ignored. For example the result of expression 5/3 is 1.

While evaluating an integer expression, the following rules are applied:

1. An operand of type byte, short or char is converted to int before evaluation of the expression. This is true for unary as well as binary operators. So the operands will always be of type int or long.

2. While evaluating expressions the operators are applied according to precedence rules, which are same as in C/C++.

3. If both the operands of an operator are of type int then result will also be of type int.

4. If both the operands of an operator are of type long then result will also be of type long.

5. If one of the operands of an operator is of type int and the other is of type long then the int would be converted to long and the result will also be of type long.

Integer division including modulo (%) operator may result in an ArithmeticException (run time error).

Example: The following example illustrates what happens on division by zero.

class ZeroDivision
{
   public static void main(String args[])

  {
     int i;

     int x = 5;

      i = x / 0;  // x % 0 will also lead to exception.

      int y = i + 3;

      System.out.println(y);

  }

}

On execution of the above program, the following exception (run time error) occurs at statement

i = x/0; and the program terminates:

Exception in thread "main" java.lang.ArithmeticException: / by zero

        at ZeroDivision.main(ZeroDivision.java:5)

The reason is that division by zero is not defined in Java when both the operands are integer. In Java, it is possible to recover from a run time error and continue the execution. This is done by using exception-handling mechanism that will be discussed later.

Overflow in Integer Expressions

Except division no other integer calculations result in any exception although the result might be arithmetically incorrect. On evaluation of an operator (or complex integer expression), the result may be outside the range of the result data type. Such a situation is called overflow.  The actual result is obtained by truncating the extra bits in the result. For example, if the type of the result is
int and the result contains more than 32 bits, then 32 least significant bits in the result are kept and rest are simply truncated to obtain the result. Thus the result will not be correct and in some cases result may be of the wrong sign.

Example:

int tooBig = Integer.MAX_VALUE + 1;

MAX_VALUE is a static final constant defined in the class Integer. Its value represents the maximum positive value that can be stored in a variable of type int, which is, 2147483647. The result of the above expression will be of type int as both the operands are of type int. But the expression would lead to overflow as we are adding one to highest integer value.

In fact, on displaying value of variable tooBig, you will find that it holds the value -2147483648, which is the minimum value that an integer can hold.  The only solution to the overflow problem is to use higher data type i.e. use long type if you expect the result to exceed the limit of the int.

3. Floating-Point Expressions

These are the expressions involving floating-point types. A floating-point expression may also have integer types i.e. a mix of integer and floating-point types. If a floating-point expression has an operator whose both the operands are integer then its result will be calculated as per integer arithmetic rules. For example the expression 3.1 + 5/3 is a floating-point expression but its result will be 4.1 (3.1 + 1) and not 4.76 (3.1 + 1.66) as one might expect because the result of the expression 5/3 would still be int.

While evaluating a floating-point expression, the following rules are applied:

  • An operand of type byte, short or char type is converted to int before evaluation of the expression. This is true for unary as well as binary operators. So the integer operands will always be of type int or long.
  • While evaluating expressions the operators are applied according to precedence rules, whichare same as in C/C++.
  • If operands of an operator are of different types then the lower type operand will be promoted to the type of the higher type operand and the type of the result will be the higher type.
There are three special floating-point values infinity, minus infinity and not a number to represent out-of-range values.

In case of float type these values are represented by constants Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY and Float.NaN respectively defined in the class Float.

Similarly in case of double type these values are represented by constants
Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY and Double.NaN respectively defined in the class Double.

No exception occurs on division by zero as this is defined in case of floating point expressions.

The result of division by zero is either Infinity or –Infinity. Similarly no error occurs if the floating-point expression involves non-determinant forms like 0/0, 0/, /, /0, complex numbers etc. The result of such an expression is NaN (Not a Number).

Examples: 
The following code segments demonstrate the use of three special values.

(i) double d = 10.0/0;

System.out.println(d);

The output of the above println() statement would be Infinity.

(ii) double d = -10.0/0;

System.out.println(d);

The output of the above println() statement would be Infinity.

(iii) double d = 0.0/0;

System.out.println(d);

The output of the above println() statement would be NaN.

(iv) double d = Math.sqrt(-25);

System.out.println(d);

The output of the above println() statement would be NaN.

Note: The result will be same if we substitute the type double with float.

Overflow in Floating-Point Expressions

If the result of a floating-point expression exceeds the maximum value (magnitude) it is represented by +Infinity or –Infinity depending on the sign of the result.

Examples:

(i) double d = 1.0E+308 * 10;

System.out.println(d);
The output of the above println() statement would be Infinity. The result exceeds the

maximum value (magnitude) and its sign is positive hence it is represented as positive

Infinity.

(ii) double d = -1.0E+308 * 10;

System.out.println(d);

The output of the above println() statement would be Infinity. The result exceeds the maximum value (magnitude) and its sign is negative hence it is represented as negative Infinity.

Note: The result will be same if we substitute the type double with float and the result exceeds the maximum float value.

Underflow in Floating-Point Expressions

If the result of a floating-point expression is less than the minimum value (magnitude), it is represented by 0 (Zero).

Examples:

(i) double d = 3E-324 – 1.0E-324;

System.out.println(d);

The output of the above println() statement would be 0 (Zero). The result is less than the minimum value (magnitude) hence it is treated as 0 (Zero).

(ii) double d =  -3E-324 – 1.5E-324;

System.out.println(d);

The output of the above println() statement would be 0 (Zero). The result is less than the minimum value (magnitude) hence it is treated as 0 (Zero).

Note: The result will be same if we substitute the type double with float and the result is less than the minimum float value.

Java Shift Operators

The Java supports the following bit-wise shift operators:

<< (left shift)

>> (right shift with sign fill)

>>> (right shift with zero fill)

Both the operands must be of integer type. The type promotion rule is applied to each operand before performing the shift operation. The type of the result is same as the promoted type of the left hand operand. This means that type of result will be either int or long.

The shift distance should be in the range 0 to 31 if the promoted type of the left hand operand is int. This means you can shift the bits to left or right by at the most 31 places.

If the shift distance is more than 31 then it will be converted to a value in the range 0 to 31 by obtaining mod with 32. So shifting by 32 is equivalent to shifting by 0 i.e. no shifting al all;

shifting by 33 is equivalent to shifting by 1, and so on.

If the shift distance is negative then actual shift distance is obtained by adding some multiple of 32 to the shift distance such that it falls in the range 0 to 31. For example, if shift distance is –30

then the actual shift distance will be 2 (-30 + 1*32); if the shift distance is –44 then the actual shift distance will be 20 (-44 + 2 * 32).

The shift distance should be in the range 0 to 63 if the promoted type of the left hand operand is long. This means you can shift the bits to left or right by at the most 63 places. If the shift distance is more than 63 then it will be converted to a value in the range 0 to 63 by obtaining mod with 64. So shifting by 64 is equivalent to shifting by 0 i.e. no shifting at all; shifting by 65 is equivalent to shifting by 1, and so on.

If the shift distance is negative then actual shift distance is obtained by adding some multiple of 64 to the shift distance such that it falls in the range 0 to 63. For example, if shift distance is –60

then the actual shift distance will be 4 (-60 + 1*64); if the shift distance is –88 then the actual shift distance will be 40 (-88 + 2 * 64).

Left Shift (<<) Operator

To obtain the result of << operator, the bits in the left hand side operand (which can be any integer expression) are shifted to the left as specified by the right hand operand (which can also be any integer expression) and the empty bit positions to the right are filled with zero.  Left shifting by 1 is equivalent to multiplication by 2. It is possible that sign of the result may differ from the sign of the left hand side operand. This may happen because the sign depends on the
left-most bit, which can change from 0 to 1 or 1 to 0 hence the change in sign.

Example:

y = x << 4

To obtain the value of y, shift the bits in x by 4 positions to the left and fill the 4 right bits with zero.

Right Shift with sign fill (>>) Operator

To obtain the result of >> operator, the bits in the left hand side operand (which can be any integer expression) are shifted to the right as specified by the right hand operand (which can also be any integer expression) and the empty bit positions to the left are filled with sign bit. Right shifting by 1 is equivalent to division 2. This operator never changes the sign of the result i.e. it will be same as the sign of the left hand operand.

Example:

y = x >> 4

To obtain the value of y, shift the bits in x by 4 positions to the right and fill the 4 left bits with sign bit (i.e. with 0 if the leftmost bit before shifting is 0 or 1 if the leftmost bit before shifting is 1).

Right Shift with zero fill (>>>) Operator

To obtain the result of >>> operator, the bits in the left hand side operand (which can be any integer expression) are shifted to the right as specified by the right hand operand (which can also be any integer expression) and the empty bit positions to the left are filled with 0.  Right shifting by 1 is equivalent to division 2. If shifting tales place then result will always be positive, as the rightmost bit would become zero.

Example:

y = x >>> 4

To obtain the value of y, shift the bits in x by 4 positions to the right and fill the 4 left bits with 0.

Java Bit-wise operators

The Java supports the following bit-wise operators:

~ (1’s complement or bit-wise complement)| (bit-wise OR)

& (bit-wise AND)

^ (bit-wise exclusive OR or XOR)

The operands of these operators must be of integer type only. If any of the operand of a bit-wise operator is of type byte, short or char then it is promoted to int before being used. If one operand is of type long then other is also promoted to long. So the type of the result will always be int or long.

Note: The operators |, & and ^ behave like boolean logical operators if both the operands are of boolean type.

Bit-wise Assignment Operators

The Java supports the following Bit-wsie Assignment Operators:

&=, |=, ^=

An assignment operator has the following syntax:

<variable><operator> = <expression>

The above assignment is equivalent to:

<variable> = <variable>  <operator> (<expression>)

For example the assignment:

 i &= i1 is equivalent to i = i & i1

Here i is an integer type variable and i1 is an integer expression.

Java Assignment Operator (=)

The assignment operator has the following syntax:

<variable> = <expression>

Here variable can be of primitive type or reference type. Similarly expression may result in a primitive data value or object reference:

Example: Assignment involving primitives.

x = 10;

Example: Assignment involving reference data type.

int x[];

x = new int [100];

Here new is an operator that returns an object reference.

Multiple Assignments

The assignment operator = may be used like any other operator to form a compound expression.

The operator can appear more than once in an assignment statement as shown below:

Example:

int x = 5, y = 6 , z = 7;

x = y = z; //multiple assignment statement

Here = behaves like an operator. It is a right associative operator, hence after the execution of the assignment statement, the value of variables x, y and z will be 7.

Example:

int  x[], y[];

x = y = new int[20];

Here new operator returns reference to an array, which can hold 20 int values. The reference is first assigned to y and then to x as the = operator is right associative.

Note: Assigning a reference does not create a copy of the object. So both x and y refer to the same array in this example.