Course outline · 0% complete

0/29 lessons0%

Course overview →

Operators and casting

lesson 2-3 · ~7 min · 6/29

Arithmetic with a twist

Arithmetic looks too basic to study until an average of test scores comes out as 79 instead of 79.5, or a discount computes to 0. Both are int-division bugs, and they ship to production every week somewhere.

The operators + - * / % work as in Python, with one big exception: dividing two ints throws away the remainder.

7 / 2    // 3, not 3.5
7 % 2    // 1, the remainder
7.0 / 2  // 3.5, because one side is a double

Python 3 gives 3.5 for 7 / 2. Java behaves like Python's // when both sides are ints, and switches to decimal division as soon as either side is a double.

The shortcuts you know from Python work too, so x += 2, x -= 1, and x *= 3 are all valid. Java adds x++ to add 1 and x-- to subtract 1, which you will see constantly in loops.

Three divisions and two shortcuts

Only the version involving a double keeps the decimal part.

public class Main {
  public static void main(String[] args) {
    System.out.println(7 / 2);
    System.out.println(7 % 2);
    System.out.println(7.0 / 2);
    int x = 10;
    x++;
    x += 5;
    System.out.println(x);
  }
}

Output

3
1
3.5
16

The first result is 3 rather than 3.5, and nothing warns you. 7 % 2 recovers the 1 that was thrown away, which is why the two operators are so often used together.

The last line shows x++ and x += 5 applied in sequence, taking 10 to 11 and then to 16. Both modify the variable in place rather than producing a value to assign.

Casting between types

A cast converts a value to another type by writing the target type in parentheses:

int a = 7;
double d = (double) a / 2;  // 3.5, a becomes 7.0 first
int chopped = (int) 3.9;    // 3, decimals are cut off, not rounded

The placement in the first line matters. (double) a / 2 casts a and then divides, while (double) (a / 2) would do the int division first and then widen the 3 that came out, giving 3.0.

Going from int to double is safe, and Java even does it automatically, as in double d = 7;. Going from double to int loses information, so Java forces you to write the cast explicitly.

That is the theme of the whole language: anything lossy has to be visible in the code.

Why money never goes in a double

A double stores numbers as binary fractions. Just as 1/3 has no exact decimal form, being 0.3333..., one tenth has no exact binary form, so 0.1 in a double is really a number extremely close to 0.1, and the tiny errors surface in arithmetic:

System.out.println(0.1 + 0.2);  // 0.30000000000000004

For physics or graphics that error is harmless, since no screen has 17 digits of precision. For money it is unacceptable, because pennies have to add up exactly and a total that is off by a fraction of a cent cannot be reconciled.

The industry-standard fix is to store money as an int or long count of cents, do exact whole-number arithmetic, and format as dollars only when printing. Integer division and % from earlier in this lesson are exactly the tools for that final formatting step.

The cents pattern

First the double error, then $10.99 plus $0.88 of tax stored as whole cents and formatted with / and %.

public class Main {
  public static void main(String[] args) {
    System.out.println(0.1 + 0.2);
    int priceCents = 1099;
    int taxCents = 88;
    int total = priceCents + taxCents;
    System.out.println(total / 100 + " dollars " + total % 100 + " cents");
  }
}

Output

0.30000000000000004
11 dollars 87 cents

The total is 1187 cents. Dividing by 100 gives the 11 whole dollars and % 100 gives the leftover 87, so the two operators split one number into the two parts a human wants to read.

Every intermediate value here is a whole number, which is the point. Adding a thousand such prices produces an exact total, while the same additions in a double would drift by a fraction of a cent that eventually rounds the wrong way.

Casting a double to an int

(int) 9.99 evaluates to 9.

Casting a double to an int truncates toward zero, cutting off the decimal part entirely rather than rounding. So 9.99 becomes 9, and -9.99 becomes -9.

For rounding, Math.round(9.99) gives 10, which is usually what a person expects.

ExpressionResult
(int) 9.999
(int) 9.019
Math.round(9.99)10
Math.round(9.01)9

The distinction bites when converting an average or a price. Truncating a computed 9.99 rating down to 9 stars is a bug that looks like a rounding preference until somebody checks the arithmetic.

Splitting minutes into hours and minutes

Integer division gives the whole hours and % gives the leftover minutes, and dividing by a decimal gives the exact figure instead.

public class Main {
  public static void main(String[] args) {
    int totalMinutes = 200;
    int hours = totalMinutes / 60;
    int minutes = totalMinutes % 60;
    System.out.println(hours + " h " + minutes + " min");
    System.out.println(totalMinutes / 60.0 + " h");
  }
}

Output

3 h 20 min
3.3333333333333335 h

200 / 60 gives 3 because both sides are ints, and 200 % 60 recovers the 20 that division discarded. Together they turn one number into the two a human reads off a clock.

The second line divides by 60.0, a double, so no cast is needed and the decimals survive. The trailing 5 in 3.3333333333333335 is the binary-fraction imprecision from earlier in this lesson showing up in the last digit.