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
- Evaluate any expression using operator precedence, and change its order with parentheses
- Avoid the integer division trap, especially when calculating averages
- Tell implicit widening from explicit casting, and place
(int)and(double)correctly - Do arithmetic with characters, and declare constants with
final - Use the methods of the
Mathclass and know what type each one returns - Explain floating-point and overflow surprises, and put
%to work on real problems
In this lecture
- Multiplication comes before addition, and parentheses come before everything
- Dividing two whole numbers gives a whole number, and the fraction is dropped
- A
doublebox cannot rescue an integer division that already finished - Java converts a value to a wider type automatically, because nothing is lost
- The compiler refuses to put a
doublein anintbecause the fraction would be lost - A cast tells the compiler: I know what I am doing
- Where the cast goes decides whether the fraction survives
- A
charreally stores a number, so you can calculate with it finallocks a value, and we write its name in capitals- The
Mathclass is ready for hard calculations and needs no import round,ceilandfloorare three different ways to remove a fraction0.1 + 0.2is not exactly0.3, andprintfhides the difference- An
intoverflows silently, even halfway through a calculation %answers real questions: even? last digit? how many seconds left?- Final exercise: the average and highest of three marks
- Review questions
Multiplication comes before addition, and parentheses come before everything
| Priority | Operators | Order 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
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);
}
}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
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);
}
}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
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);
}
}4.5
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.
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);
}
}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);
}
}incompatible types: possible lossy conversion from double to intShow the fix
public class Price {
public static void main(String[] args) {
double price = 12.75;
double copy = price;
System.out.println(copy);
}
}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?
5.0 is a double even though its fraction is zero. The last two lines are valid: 5 / 2 is an int, which fits in both boxes.A cast tells the compiler: I know what I am doing
(int) drops the fractionIt never rounds: 12.99 becomes 12, and -3.99 becomes -3.
int whole = (int) 12.99;(double) adds a fractionThe 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);
}
}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
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
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);
}
}possible lossy conversion from double to intShow the fix
public class WholeAverage {
public static void main(String[] args) {
int sum = 9;
int average = (int) (sum / 2.0);
System.out.println(average);
}
}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
}
}4 5
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
| Character | Code |
|---|---|
'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');
}
}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
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);
}
}possible lossy conversion from int to charShow the fix
public class NextLetter {
public static void main(String[] args) {
char letter = 'A';
char next = (char) (letter + 1);
System.out.println(next);
}
}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.
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);
}
}16.0 116.0
| Name | Used for | Convention |
|---|---|---|
studentAge | A variable | camelCase |
TAX_RATE | A constant | Capitals, with _ between words |
MAX_STUDENTS | A constant | Capitals, 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);
}
}cannot assign a value to final variable TAX_RATEShow the fix
public class FinalRate {
public static void main(String[] args) {
final double TAX_RATE = 0.16;
System.out.println(TAX_RATE);
}
}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
| Usage | Meaning | Result | Type |
|---|---|---|---|
Math.pow(2, 10) | 2 to the power 10 | 1024.0 | double |
Math.sqrt(16) | Square root | 4.0 | double |
Math.abs(-7) | Absolute value | 7 | Same as argument |
Math.max(3, 9) | The larger | 9 | Same as argument |
Math.min(3, 9) | The smaller | 3 | Same as argument |
Math.PI | A ready constant | 3.141592653589793 | double |
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);
}
}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);
}
}possible lossy conversion from double to intShow the fix
public class Power {
public static void main(String[] args) {
int result = (int) Math.pow(2, 3);
System.out.println(result);
}
}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
| Method | Does | 2.1 | 2.5 | -2.5 | Returns |
|---|---|---|---|---|---|
Math.round | To the nearest | 2 | 3 | -2 | long |
Math.ceil | Up | 3.0 | 3.0 | -2.0 | double |
Math.floor | Down | 2.0 | 2.0 | -3.0 | double |
(int) | Drops the fraction | 2 | 2 | -2 | int |
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
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);
}
}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
}
}1
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);
}
}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
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
-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);
}
}-1141367296 3153600000
% answers real questions: even? last digit? how many seconds left?
The remainder after dividing by 2 is 0 for even and 1 for odd.
n % 2The remainder after dividing by 10 is the last digit, and integer division by 10 removes it.
n % 10
n / 10Integer division gives the full minutes, and the remainder gives the seconds left over.
total / 60
total % 60Take 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
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
}
}185
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
}
}80 90 85
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?
b.Why does the compiler reject int r = Math.sqrt(16);?
Which declaration follows the right convention for a constant?
final is what prevents change, and the capital name tells the reader so. The middle option works but hides that it is a constant, and the last looks constant with no protection.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
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.