What will you build

You will test the different type of casting of variables

What You'll Learn

What will you need

Widening casting (automatic): it is the action to convert a smaller type to a larger type size:

byte -> short -> char -> int -> long -> float -> double

Narrowing casting (manually): it is the action to convert a larger type to a smaller type:

double -> float -> long -> int -> char -> short -> byte

Syntax to convert (typeName) expression

int a = 5;
double b = (double)a;

The conversion is not definitive !!!!!

First create a folder name casting_exercise_java

In this folder, create a file Casting.java

Re-write the following code and save the file.

public class Casting {

    public void intToDouble(int a){
        System.out.println("intToDouble");
        System.out.println("variable a is an int "+a);
        double b = a;
        System.out.println("variable b automatic casting in Double "+b);
        System.out.println("variable a is again a int "+a);
    }
}

Implement a second class in the file: TestCode.java

In this new class, implement a main method. In the main method, declare a variable of type Casting. Instantiate an object and initialise it with the default constructor.

Invoke the method you did implement above with the instance of your class Casting.

public class TestCode {

    public static void main(String[] args) {
	Casting itd = new Casting();
	itd.intToDouble(5);
    }
}

Compile and run this example.

You will constat that the variable a type (int) cast automatically to double.

In the file Casting.java, implement the following method. To caste a variable from an int to a float is not done automatically. You need to specify the type you want to cast.

public void showingFloatDivision(int x, int y) {
        System.out.println("Casting to ensure float division:");
        System.out.println("int x "+x);
        System.out.println("int y "+y);
        System.out.println("x / y         : " + (x / y));
        System.out.println("(float) x / y : " + (x / (float) y));
        System.out.println();
    }

In the second class TestCode.java, invoke the second method using the same Casting instance.

public class TestCode {

    public static void main(String[] args) {
	Casting itd = new Casting();
	itd.intToDouble(25);
	itd.showingFloatDivision(2,4);
    }
}

Compile and run this example.

You will constat that in this second method (showingFloatDivision) if you divise two integers together, the result will be truncated if the result was a decimal numbers.

We can also constat that the casting to the result of the division to float is working.

In the file Casting.java, continue to implement the following method:

  public void doesntRound(){
        float c = 0.7f;
        System.out.println( "Casting truncates - doesn't round:" );
        System.out.println( "c      : " + c );
        System.out.println( "(int)c : " + (int) c );
        System.out.println();
    }

In the second class TestCode.java, invoke the second methods in the same instance of Casting that you invoke the first and second methods (previous examples) in the main method of TestCode.java

public class TestCode {

    public static void main(String[] args) {
	Casting itd = new Casting();
	itd.intToDouble();
	itd.intToDouble(25);
	itd.showingFloatDivision(2,4);
    }
}

Compile and run this example.

You can see that the casting is not rounding the number but truncating it. If you want to round a number, you need to use the Math.round() method.

see the Oracle documentation for Math.round()

In the file Casting.java, continue to implement the following method:

	public void onlyTemp(){
        System.out.println(
                "The type is only changed for the current operation");
        float h = 15.264f;
        System.out.println( "h      : " + h );
        System.out.println( "(int)h : " + (int)h);
        System.out.println( "h      : " + h );
        }

In the second class TestCode.java, invoke the method in the same instance of Casting that you invoke the first and second methods (previous examples) in the main method of TestCode.java

public class TestCode {

    public static void main(String[] args) {
	Casting itd = new Casting();
	itd.intToDouble(25);
	itd.showingFloatDivision(2,4);
	itd.doesntRound();
	itd.onlyTemp();
    }
}

Compile and run this example.

You can see that the casting is only temporary.

In the file Casting.java, continue to implement the following method:

 public int roundOffError() {
        double f = 4.35;
        int n = (int) (100 * f);
        return n;
    }
}

In the second class TestCode.java, invoke the method in the same instance of Casting that you invoke the previous methods in the main method of TestCode.java

public class TestCode {

    public static void main(String[] args) {
	Casting itd = new Casting();
	itd.intToDouble(25);
	itd.showingFloatDivision(2,4);
	itd.doesntRound();
	itd.onlyTemp();
	int roundOff = itd.roundOffError();
	System.out.println(roundOff);
    }
}

Compile and run this example.

You can observe the result. What is happening there, is the roundOffError. Computer store real numbers with the a limited precision. Rounding errors are due to inexactness in the representation of real numbers and the arithmetic operations done with them. Depending of the type the value is stored with, the precision could change.

The consequances of the error roundoff could be catastrophic. For example, in 1995, Ariane 5 was launched and exploded forty seconds after the lift-off. A 64 bit floating point number have been converted to a 16 bit integer. The value was too large to be stored by a integer, and the conversion failed.

You can test the following code to see what did happen.

public class ArianeMistake {

public void arianeMistakeReproduced(){
double d = 4.35*1000000000;
	short s = (short)d;
	System.out.println("print the s "+s);
	//System.out.println("print the f "+d);
	System.out.printf("print the f: %.0f\n", d);
	}
}

In the TestCode class, you will declare, instantiate and initialise a variable of type ArianeMistake and invoke the method on this instance.

public class TestCode {

    public static void main(String[] args) {
	//Casting itd = new Casting();
	//itd.intToDouble(25);
	//itd.showingFloatDivision(2,4);
	//itd.doesntRound();
	//itd.onlyTemp();
	//int roundOff = itd.roundOffError();
	//System.out.println(roundOff);
	
	ArianeMistake am = new ArianeMistake();
	am.arianeMistakeReproduced();
    }
}

Some of the disasters caused by numerical errors:

http://ta.twi.tudelft.nl/users/vuik/wi211/disasters.html

In this example, we will observe the issue of overflow. Overflow is what is happening when the value is too big for the type of variable that hold it. Run the following code, and observe what is happening.

public class OverFlow{

public void overFlowInt(){
	int a = 2000_000_000;
	int b = 147483648;
	System.out.println("a+b :"+(a+b));
	}
	
public int printMaxValueForInteger(){
	return Integer.MAX_VALUE;
	}

public int printMinValueForInteger(){
	int value = Integer.MIN_VALUE;
	return value;
	}
}

In the TestCode class, you will declare, instantiate and initialise a variable of type OverFlow with the default constructor and invoke the methods of this class on this instance.

public class TestCode {

    public static void main(String[] args) {
	//Casting itd = new Casting();
	//itd.intToDouble(25);
	//itd.showingFloatDivision(2,4);
	//itd.doesntRound();
	//itd.onlyTemp();
	//int roundOff = itd.roundOffError();
	//System.out.println(roundOff);
	
	//ArianeMistake am = new ArianeMistake();
	//am.arianeMistakeReproduced();
	
	OverFlow of = new OverFlow();
	of.overFlowInt();
	int minValue = of.printMaxValueForInteger();
	int maxValue = of.printMinValueForInteger();	
    }
}

We know that the biggest value of an Integer is 2147483647. However, 2000000000+147483648 = 2147483648, which is too big to Integer. Overflow did happen and instead of having the correct number, we have -2147483648.

The overflow errors will happen in all types of primitives, at a certain point the value is too big, or too small, and will overflow. One of the solution is to use BigInteger, BigDecimal, AtomicInteger, ....

To be able to work with BigInteger we need to import the library.

BigInteger is not a primitive, by consequence, we need to declare an object BigInteger, instantiate and initialise the variable of type BigInteger.

The constructor of the BigInteger can take different type of variable, but cannot take a simple integer as a parameter of its constructor.

import java.math.BigInteger; 

After importing the library we can declare the class

import java.math.BigInteger;
public class NotOverFlow{

public BigInteger shouldNotOverFlow(){
	BigInteger bigA = new BigInteger("2000000000");
	BigInteger bigB = new BigInteger("147483648");
	BigInteger sum = bigA.add(bigB);
	return sum;
	}
}

In the TestCode class, you will declare, instantiate and initialise a variable of type NotOverFlow with the default constructor and invoke the method of this class on this instance.

public class TestCode {

    public static void main(String[] args) {
	NotOverFlow notOver = new NotOverFlow();
	BigInteger total = notOver.shouldNotOverFlow();
	System.out.println("Sum of the bigInteger "+total);
    }
}

To see more about big numbers please look at the documentation: Number Oracle's documentation

You can find the code for this example in https://git.cardiff.ac.uk/ASE_GROUP_2020/code_for_codelabs.git