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.
| Expression | Result |
|---|---|
(int) 9.99 | 9 |
(int) 9.01 | 9 |
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.