University College of Applied SciencesProgramming 1
العربية
Lecture 6 · 100 min

Expressions, Casting and Math

Why does 5 / 2 give 2, and how do you make Java calculate exactly what you mean?

By the end of this lecture you will be able to

  1. Evaluate any expression using operator precedence, and change its order with parentheses
  2. Avoid the integer division trap, especially when calculating averages
  3. Tell implicit widening from explicit casting, and place (int) and (double) correctly
  4. Do arithmetic with characters, and declare constants with final
  5. Use the methods of the Math class and know what type each one returns
  6. Explain floating-point and overflow surprises, and put % to work on real problems
In this lecture
  1. Multiplication comes before addition, and parentheses come before everything
  2. Dividing two whole numbers gives a whole number, and the fraction is dropped
  3. A double box cannot rescue an integer division that already finished
  4. Java converts a value to a wider type automatically, because nothing is lost
  5. The compiler refuses to put a double in an int because the fraction would be lost
  6. A cast tells the compiler: I know what I am doing
  7. Where the cast goes decides whether the fraction survives
  8. A char really stores a number, so you can calculate with it
  9. final locks a value, and we write its name in capitals
  10. The Math class is ready for hard calculations and needs no import
  11. round, ceil and floor are three different ways to remove a fraction
  12. 0.1 + 0.2 is not exactly 0.3, and printf hides the difference
  13. An int overflows silently, even halfway through a calculation
  14. % answers real questions: even? last digit? how many seconds left?
  15. Final exercise: the average and highest of three marks
  16. Review questions
Part one: how Java calculates

Multiplication comes before addition, and parentheses come before everything

PriorityOperatorsOrder among equals
First( )Inside out
Second++ -- (int) (double)Right to left
Third* / %Left to right
Fourth+ -Left to right
Last= += -= *= /=Right to left

Work out each line on paper first. What does the program print?

public class Precedence {
    public static void main(String[] args) {
        System.out.println(2 + 3 * 4);
        System.out.println((2 + 3) * 4);
        System.out.println(20 / 2 * 5);
        System.out.println(20 / (2 * 5));
        System.out.println(10 + 7 % 3);
    }
}
Show the output
Output
14
20
50
2
11

Why? * comes before +, and parentheses reverse that. / and * are equal, so we start from the left: 20 ÷ 2 = 10, then × 5 = 50. % sits at the level of multiplication: 7 % 3 = 1, then 10 + 1.

One pair of parentheses changes the answer

What is the average of 80 and 90? Compare the two lines, then change the values to 60 and 100 and predict the output before running.

public class Average {
    public static void main(String[] args) {
        int a = 80;
        int b = 90;
        System.out.println(a + b / 2);
        System.out.println((a + b) / 2);
    }
}
Output
125
85

Dividing two whole numbers gives a whole number, and the fraction is dropped

Java decides the result type from the types of both sides before calculating: int / int gives int. If either side is a double, the result is a double.

The numbers look alike, but the results do not. What does the program print?

public class Division {
    public static void main(String[] args) {
        System.out.println(5 / 2);
        System.out.println(5.0 / 2);
        System.out.println(5 / 2.0);
        System.out.println(1 / 2);
    }
}
Show the output
Output
2
2.5
2.5
0

Why? The fraction is dropped, not rounded: 5 / 2 gives 2, not 3. One double side is enough to keep the fraction.

Pizza and friends

Share 7 slices between 4 friends. Try other values, and find a case where integer division prints 0.

public class Pizza {
    public static void main(String[] args) {
        int slices = 7;
        int friends = 4;
        System.out.println(slices / friends);
        System.out.println(slices % friends);
        System.out.println(slices / 4.0);
    }
}
Output
1
3
1.75

What is the value of 9 / 10?

A double box cannot rescue an integer division that already finished

The true average of 4 and 5 is 4.5. What does the program print?

public class AverageTrap {
    public static void main(String[] args) {
        int a = 4;
        int b = 5;
        double average = (a + b) / 2;
        System.out.println(average);
    }
}
Show the output
Output
4.0

Why? The right side is calculated first, without looking at the box: 9 / 2 = 4 by integer division. Only then is 4 stored in a double and becomes 4.0. The fraction was lost before reaching the box.

The easiest fix: divide by 2.0

Change 2.0 to 2 and notice the difference. Then add a third number c and update the formula.

public class AverageFixed {
    public static void main(String[] args) {
        int a = 4;
        int b = 5;
        double average = (a + b) / 2.0;
        System.out.println(average);
    }
}
Output
4.5
Part two: converting between types

Java converts a value to a wider type automatically, because nothing is lost

A double can hold any int value, and an int can hold the code of any char. So Java moves the value to the wider box without being asked. This is called implicit widening. From narrowest to widest: char, then int, then long, then double.

whole7int
decimal7.0double

Widening at work

Press "Debug line by line" and compare the values of the whole and decimal boxes.

public class Widening {
    public static void main(String[] args) {
        int whole = 7;
        double decimal = whole;
        System.out.println(decimal);
        System.out.println(whole + 0.5);

        char letter = 'A';
        int code = letter;
        System.out.println(code);
    }
}
Output
7.0
7.5
65

What is the type of 3 + 2.0?

The compiler refuses to put a double in an int because the fraction would be lost

Widening runs in one direction only. If a conversion could lose part of the value, the compiler stops you and asks you to decide for yourself.

A price in a whole-number box

We want to keep the whole price. Fix the variable type so the program prints 12.75.

public class Price {
    public static void main(String[] args) {
        double price = 12.75;
        int copy = price;
        System.out.println(copy);
    }
}
Compiler messageincompatible types: possible lossy conversion from double to int
Show the fix
public class Price {
    public static void main(String[] args) {
        double price = 12.75;
        double copy = price;
        System.out.println(copy);
    }
}
Output
12.75

Why? "Lossy" means something could be lost, here the .75. The compiler is just as careful even when the value is 12.0, because it looks at the type, not the value.

Which line does the compiler reject?

A cast tells the compiler: I know what I am doing

(int) drops the fraction

It never rounds: 12.99 becomes 12, and -3.99 becomes -3.

int whole = (int) 12.99;
(double) adds a fraction

The value stays the same, but its type changes, and so does how it calculates.

double d = (double) 5;

Try casting

Notice that price did not change after the cast: a cast makes a new value and leaves the original box alone. Press "Debug line by line" to confirm.

public class Casts {
    public static void main(String[] args) {
        double price = 12.99;
        int whole = (int) price;
        System.out.println(whole);
        System.out.println(price);
        System.out.println((int) -3.99);
        System.out.println((double) 5);
    }
}
Output
12
12.99
-3
5.0

The difference is one pair of parentheses. What does the program print?

public class CastScope {
    public static void main(String[] args) {
        System.out.println((int) 3.7 + 0.9);
        System.out.println((int) (3.7 + 0.9));
    }
}
Show the output
Output
3.9
4

Why? A cast outranks addition, so it touches only the value right after it. In line 1, 3.7 becomes 3, then + 0.9. In line 2 the values are added first to get 4.6, then the fraction is dropped.

Where the cast goes decides whether the fraction survives

Three places for the same cast. What does the program print?

public class CastPlace {
    public static void main(String[] args) {
        int a = 5;
        int b = 2;
        System.out.println((double) a / b);
        System.out.println(a / (double) b);
        System.out.println((double) (a / b));
    }
}
Show the output
Output
2.5
2.5
2.0

Why? In the first two lines one side is cast before dividing, so it is double division. In the third, integer division runs first inside the parentheses and gives 2, and the cast can then only make it 2.0.

An average in a whole-number box

We want to drop the fraction on purpose and keep the whole part of the average. Use a cast so the program prints 4.

public class WholeAverage {
    public static void main(String[] args) {
        int sum = 9;
        int average = sum / 2.0;
        System.out.println(average);
    }
}
Compiler messagepossible lossy conversion from double to int
Show the fix
public class WholeAverage {
    public static void main(String[] args) {
        int sum = 9;
        int average = (int) (sum / 2.0);
        System.out.println(average);
    }
}
Output
4

Why? sum / 2.0 is a double, so it cannot go in an int without permission. Note the parentheses: without them the cast applies only to sum, the result stays a double, and the error remains.

An exact average of two whole numbers

Print First: and read a whole number into an int, then Second: and read another. Then print Average: followed by the exact average, using a cast.

import java.util.Scanner;

public class ExactAverage {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        // Read two int values, then print their exact average

    }
}
stdin
4
5
Expected output
First: Second: Average: 4.5
Hint 1

Add first inside parentheses, then cast the sum: (double) (a + b).

Hint 2

If the program prints 4.0, your cast came after the division.

Solution
import java.util.Scanner;

public class ExactAverage {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("First: ");
        int a = input.nextInt();
        System.out.print("Second: ");
        int b = input.nextInt();
        double average = (double) (a + b) / 2;
        System.out.println("Average: " + average);
    }
}

A char really stores a number, so you can calculate with it

CharacterCode
'A'65
'B'66
'a'97
'0'48

Arithmetic with characters

Change letter to 'Z' and then to 'a', and predict the output before each run.

public class CharMath {
    public static void main(String[] args) {
        char letter = 'A';
        System.out.println(letter + 1);
        System.out.println((char) (letter + 1));
        letter++;
        System.out.println(letter);
        System.out.println('7' - '0');
        System.out.println('a' - 'A');
    }
}
Output
66
B
B
7
32

What makes the two lines different?

public class CharPlus {
    public static void main(String[] args) {
        System.out.println('A' + 'B');
        System.out.println("" + 'A' + 'B');
    }
}
Show the output
Output
131
AB

Why? + between two characters adds them as numbers: 65 + 66. With a string, even an empty one, it means joining. Reading left to right also explains why the second character gets joined too.

The next letter

We want the letter after A. Fix the line so the program prints B.

public class NextLetter {
    public static void main(String[] args) {
        char letter = 'A';
        char next = letter + 1;
        System.out.println(next);
    }
}
Compiler messagepossible lossy conversion from int to char
Show the fix
public class NextLetter {
    public static void main(String[] args) {
        char letter = 'A';
        char next = (char) (letter + 1);
        System.out.println(next);
    }
}
Output
B

Why? char + int gives an int, and int is wider than char, so we need a cast. This is the same error we saw with double, between a different pair of types.

Part three: constants, `Math` and surprises

final locks a value, and we write its name in capitals

The number 0.16 in the middle of a formula tells a reader nothing. TAX_RATE explains itself, and if the tax changes, we edit one line.

One constant, used several times

Change TAX_RATE to 0.2 on one line, and see how every result changes.

public class Tax {
    public static void main(String[] args) {
        final double TAX_RATE = 0.16;
        double price = 100.0;
        double tax = price * TAX_RATE;
        System.out.println(tax);
        System.out.println(price + tax);
    }
}
Output
16.0
116.0
NameUsed forConvention
studentAgeA variablecamelCase
TAX_RATEA constantCapitals, with _ between words
MAX_STUDENTSA constantCapitals, with _ between words

A constant does not change

We do not want to change the tax here. Delete the line that breaks that rule.

public class FinalRate {
    public static void main(String[] args) {
        final double TAX_RATE = 0.16;
        TAX_RATE = 0.2;
        System.out.println(TAX_RATE);
    }
}
Compiler messagecannot assign a value to final variable TAX_RATE
Show the fix
public class FinalRate {
    public static void main(String[] args) {
        final double TAX_RATE = 0.16;
        System.out.println(TAX_RATE);
    }
}
Output
0.16

Why? This is the point of final: the compiler stops you from changing the value by accident, rather than letting you discover it later through wrong results.

The Math class is ready for hard calculations and needs no import

UsageMeaningResultType
Math.pow(2, 10)2 to the power 101024.0double
Math.sqrt(16)Square root4.0double
Math.abs(-7)Absolute value7Same as argument
Math.max(3, 9)The larger9Same as argument
Math.min(3, 9)The smaller3Same as argument
Math.PIA ready constant3.141592653589793double

A tour of Math

Notice that Math.PI is in capitals because it is a constant, with no parentheses because it is not a method. Change the values and predict the output.

public class MathTour {
    public static void main(String[] args) {
        System.out.println(Math.pow(2, 10));
        System.out.println(Math.sqrt(16));
        System.out.println(Math.abs(-7));
        System.out.println(Math.max(3, 9));
        System.out.println(Math.min(3, 9));
        System.out.println(Math.PI);
    }
}
Output
1024.0
4.0
7
9
3
3.141592653589793

The hypotenuse of a right triangle

Try 3 and 4, then 5 and 12, then values of your own. The calculation depends on a and b as you typed them.

import java.util.Scanner;

public class Hypotenuse {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("Side a: ");
        double a = input.nextDouble();
        System.out.print("Side b: ");
        double b = input.nextDouble();
        double c = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
        System.out.println("Hypotenuse: " + c);
    }
}

pow returns a double

Fix the line with a cast so the program prints 8.

public class Power {
    public static void main(String[] args) {
        int result = Math.pow(2, 3);
        System.out.println(result);
    }
}
Compiler messagepossible lossy conversion from double to int
Show the fix
public class Power {
    public static void main(String[] args) {
        int result = (int) Math.pow(2, 3);
        System.out.println(result);
    }
}
Output
8

Why? The values 2 and 3 are whole numbers, but what decides the type is what the method returns: Math.pow always returns a double.

round, ceil and floor are three different ways to remove a fraction

MethodDoes2.12.5-2.5Returns
Math.roundTo the nearest23-2long
Math.ceilUp3.03.0-2.0double
Math.floorDown2.02.0-3.0double
(int)Drops the fraction22-2int

Compare the methods. What does the program print?

public class Rounding {
    public static void main(String[] args) {
        double value = 2.7;
        System.out.println(Math.round(value));
        System.out.println(Math.ceil(value));
        System.out.println(Math.floor(value));
        System.out.println((int) value);
    }
}
Show the output
Output
3
3.0
2.0
2

Why? round gives a whole number, while ceil and floor always give a double, which is why .0 appears. Do not confuse floor with (int): they agree on positive numbers and differ on negative ones.

When do we need ceil?

There are 23 students and 4 seats in each car. How many cars do we need? Notice the (double) before the division. Then change the students to 21 and swap ceil for round: why is one student left without a seat?

public class Cars {
    public static void main(String[] args) {
        int students = 23;
        int seats = 4;
        double cars = Math.ceil((double) students / seats);
        System.out.println(cars);
    }
}
Output
6.0

Circle area

Print Radius: and read the radius as a decimal number, then print Area: followed by the area with two decimal places. Use Math.PI and Math.pow.

import java.util.Scanner;

public class CircleArea {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        // Read the radius, calculate the area, print with printf

    }
}
stdin
1
Expected output
Radius: Area: 3.14
Hint 1

The area is Math.PI * Math.pow(radius, 2).

Hint 2

Use the template "Area: %.2f%n".

Solution
import java.util.Scanner;

public class CircleArea {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("Radius: ");
        double radius = input.nextDouble();
        double area = Math.PI * Math.pow(radius, 2);
        System.out.printf("Area: %.2f%n", area);
    }
}

0.1 + 0.2 is not exactly 0.3, and printf hides the difference

Does the computer get addition wrong?

Predict the first line before running. Notice that printf changed only how it looks: line 3 proves total did not change.

public class FloatSurprise {
    public static void main(String[] args) {
        double total = 0.1 + 0.2;
        System.out.println(total);
        System.out.printf("%.2f%n", total);
        System.out.println(total);
        System.out.println(Math.round(total * 100) / 100.0);
    }
}
Output
0.30000000000000004
0.30
0.30000000000000004
0.3

You cannot write 1 ÷ 3 exactly in decimal: 0.333 and it never ends. In the same way, 0.1 cannot be written exactly in the binary the computer uses, so the nearest possible number is stored. All programming languages behave this way; it is not a Java bug.

printf rounds, and (int) cuts. What does the program print?

public class RoundOrCut {
    public static void main(String[] args) {
        double value = 2.96;
        System.out.printf("%.1f%n", value);
        System.out.println((int) value);
        System.out.println(1.1 * 3);
    }
}
Show the output
Output
3.0
2
3.3000000000000003

Why? printf shows the value rounded to one decimal place, while (int) drops the whole fraction. The last line is another surprise of the same kind, because 1.1 is also stored approximately.

An int overflows silently, even halfway through a calculation

In the variables lecture we saw that adding 1 to the largest int gives a negative number. The compiler does not object, and the program does not stop: a wrong number simply appears.

A long can hold a million × a million. What does the program print?

public class Overflow {
    public static void main(String[] args) {
        long wrong = 1000000 * 1000000;
        long right = 1000000L * 1000000;
        System.out.println(wrong);
        System.out.println(right);
    }
}
Show the output
Output
-727379968
1000000000000

Why? It is the same trap as the average: the right side is calculated before looking at the box. The first multiplication is int * int and overflows before reaching long. The L makes one side a long, so the whole multiplication happens in the wider type.

How many seconds in your life?

The first line looks fine, but it went past the int limit. Delete the L from the second line and run it, then change the age to your own.

public class Seconds {
    public static void main(String[] args) {
        int age = 100;
        int shortAnswer = age * 365 * 24 * 60 * 60;
        long longAnswer = age * 365L * 24 * 60 * 60;
        System.out.println(shortAnswer);
        System.out.println(longAnswer);
    }
}
Output
-1141367296
3153600000

% answers real questions: even? last digit? how many seconds left?

Even or odd

The remainder after dividing by 2 is 0 for even and 1 for odd.

n % 2
Last digit

The remainder after dividing by 10 is the last digit, and integer division by 10 removes it.

n % 10
n / 10
Minutes and seconds

Integer division gives the full minutes, and the remainder gives the seconds left over.

total / 60
total % 60

Take a number apart

Type 12345, then 7, then 100. What does Even check tell you about the number? You will need this idea in an if condition soon.

import java.util.Scanner;

public class Digits {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("A whole number: ");
        int number = input.nextInt();
        System.out.println("Even check: " + number % 2);
        System.out.println("Last digit: " + number % 10);
        System.out.println("Without last digit: " + number / 10);
    }
}

Think through each line: what does the program print?

public class ModuloUses {
    public static void main(String[] args) {
        int number = 2468;
        System.out.println(number % 2);
        System.out.println(number % 10);
        System.out.println(number % 100);
        System.out.println(number / 100);
    }
}
Show the output
Output
0
8
68
24

Why? % 10 gives the last digit, and % 100 gives the last two. / 100 is integer division that removes the last two. More zeros mean more digits.

Minutes and seconds

Print Seconds: and read a whole number, then print the result as 3 min 5 sec. For example, 185 seconds is 3 minutes and 5 seconds.

import java.util.Scanner;

public class SplitSeconds {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        // Read the total, split it with / and %, then print

    }
}
stdin
185
Expected output
Seconds: 3 min 5 sec
Hint 1

There are 60 seconds in a minute, so the minutes are total / 60.

Hint 2

The seconds left over are total % 60.

Solution
import java.util.Scanner;

public class SplitSeconds {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("Seconds: ");
        int total = input.nextInt();
        int minutes = total / 60;
        int seconds = total % 60;
        System.out.println(minutes + " min " + seconds + " sec");
    }
}

Final exercise: the average and highest of three marks

Marks summary

Read three whole-number marks with the prompts Mark 1: , Mark 2: and Mark 3: . Then print Average: followed by the average with two decimal places on one line, and Highest: followed by the top mark on the next.

import java.util.Scanner;

public class MarksSummary {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        // Read three marks, then print the average and the highest

    }
}
stdin
80
90
85
Expected output
Mark 1: Mark 2: Mark 3: Average: 85.00
Highest: 90
Hint 1

Divide by 3.0, or the fraction is lost before it reaches printf.

Hint 2

Math.max takes only two values, but one call can go inside another: Math.max(a, Math.max(b, c)).

Solution
import java.util.Scanner;

public class MarksSummary {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("Mark 1: ");
        int a = input.nextInt();
        System.out.print("Mark 2: ");
        int b = input.nextInt();
        System.out.print("Mark 3: ");
        int c = input.nextInt();
        double average = (a + b + c) / 3.0;
        int highest = Math.max(a, Math.max(b, c));
        System.out.printf("Average: %.2f%n", average);
        System.out.println("Highest: " + highest);
    }
}

Review questions

Which line gives the exact average of a and b, when both are int?

Why does the compiler reject int r = Math.sqrt(16);?

Which declaration follows the right convention for a constant?

Bring together what you learned today. What does the program print?

public class ReviewMixed {
    public static void main(String[] args) {
        int a = 7;
        int b = 2;
        System.out.println(a / b + 0.5);
        System.out.println((double) a / b + 0.5);
        System.out.println(a % b * 10);
    }
}
Show the output
Output
3.5
4.0
10

Why? Line 1: 7 / 2 = 3 by integer division, then + 0.5. Line 2: (double) a converts before dividing, giving 3.5, then + 0.5. Line 3: % and * are equal, so from the left: 7 % 2 = 1, then × 10.