Mathematicians distinguish between different kinds of numbers such as integers and real numbers for purposes of classification. Computer programmers need to distinguish between different kinds of numbers because numbers can be represented different ways in a computer and can thus support different operations or the same operations with slightly different meanings.
For the AP exam you only need to know about two of Java's numeric data types: ints and doubles.
The int data type, which you can probably guess is named for integer is used to represent integer values, i.e. positive and negative numbers with no fractional part such as 3, 0, -76, and 20393.
The double data type is used to represent non-integer numbers like 6.3 -0.9, and 60293.93032. Computer people call these “floating point” numbers because the decimal point “floats” relative to the magnitude of the number, similar to the way it does in scientific notation like \(6.5 ✕ 10^8\). The name double comes from the fact that doubles are represented using 64 bits, double the 32 bits used for the type float which used to be the normal size floating point number when most computers did math in units of 32-bits. (float is rarely used these days and is not part of the AP curriculum.)
In order to use int and double values in expressions we need to know how to write their literal values. Luckily it’s pretty much what you are used to:
Literal ints are made up of just digits e.g. 1 or 23. Negative integers can be expressed with a - before a literal int. We can’t use commas to group digits as in, 1,000,000 but we can use an _ instead of a comma: 1_000_000 represents the same value as 1000000 but is a lot easier to read. (This also points out another important difference between literal values and the actual values they represent: there may be different literals values, such as 1_000_000 and 1000000 that both represent the same actual value. There is no way once the program is running to know how the value was written. Two other ways to write that same value that are also legal in Java are as a hexadecimal literal 0xf4240 and a binary literal 0b11110100001001000000 though you do not need to know about those for the AP Exam.)
Literal doubles are written with digits and a decimal point: 1.0, 1.4142, 0.5. They can also be written in a form a “computer scientific notation”: 1.23e6 is a the Java version of what we’d write in scientific notation as \(1.23 × 10^6\), also known as 1_230_000.0. Finally, for values less than 1.0 a leading 0 is not required but it’s often a bit more readable to include it, writing, for instance, 0.5 rather than just .5. That’s a matter of taste though.
Note that because int and double are different data types their literal values produce different values even when they’re mathematically the same. For instance 1 and 1.0 represent different actual values; the first is an int and the second is a double. We’ll see in the next section when this difference matters.
The set of operators that operate on ints and doubles are the standard arithmetic operations you learned about in elementary school: addition (+), subtraction (-), and division (/), and multiplication (*). Multiplication is written in Java as *, as it is in many programming languages, because until recently most computers’ character sets didn’t have a character for a real multiplication sign, ×, and keyboards still don’t have a key for it. Likewise the symbol ÷ is not an operator in Java. 1
Each of these operators take two operands, the values that the operator uses when computing a new value. When our program runs the operator will operate on two actual values but in our program we write the operands as expressions that produces values of the right types. For instance these are all arithmetic expressions:
1 + 2
10 - 20
1.23 / 4.56
100 * 256
4 * 3 + 10
In the first four expressions there is just one operator each and the operands are all literal values. The last expression is what is called a compound expression being an addition expression one of whose operands is another expression, in this case the multiplication expression, 4 * 3.
The reason that expression is interpreted as 4 * 3 plus 10 rather than 4 times 3 + 10 is because the order of operations in Java follows is similar to the PEMDAS order you probably learned in middle school: multiplication and division happen before addition and subtraction. And, as in PEMDAS, we can use parentheses to group expressions to change the normal order. If we wanted the addition to happen first above, we could write 4 * (3 + 10). It also doesn't hurt to put in extra parentheses if you are unsure as to what will be done first or just to make it more clear.
Even though the set of arithmetic operators that operate on ints and doubles are the same, their meanings are subtly different depending on the types of values they are operating on. There are a two main rules:
An arithmetic operator operating on expressions of the same type (i.e. both int or both double will produce a value of that same type.
An arithmetic operator operating on mixed types (i.e. one int and one double) will produce a double. We sometimes say that doubles are contagious. When an arithmetic operator is applied to one int and one double the int value is first converted to the equivalent double value, such as 1 being converted to 1.0.
For all the arithmetic operators except division this is simple: add two ints and you get another int; two doubles and you get a double; no big deal. But since int values have no fractional part what happens when we evaluate an expression like 10 / 4? Mathematically the result is 2.5 but that can’t be represented by an int. So there’s a special rule for division: the / operator does what’s called a truncating division meaning it just lops off the part of the mathematical value after the decimal point. Thus 10 / 4 produces the int value 2. Note that this is not the same as rounding to the nearest int: the expression 19 / 10 evaluates to 1 even though the mathematical value is much closer to 2 than 1. But truncating 1.9 throws away the 0.9 giving us 1. It works the same for negative numbers: -19 / 10 truncates to -1.
For doubles division works much more like we’d expect from math but not exactly. 10.0 / 4.0 gives us 2.5 as we’d expect. However not all results can be represented exactly, . 1.0 / 3.0 gives us 0.3333333333333333 which is pretty close but obviously not exactly the same as the mathematically correct value. This is because doubles are very much like scientific notation, meant to deal with both very small and very large values to a reasonable but not infinite level of precision. In practice doubles can represent value with approximately fifteen decimal places of accuracy.
Because of the differences between int and double division we need to be careful about “double contagion” in division expressions: 1 / 4 gives us 0 but 1.0 / 4 or 1 / 4.0 give 0.25.
Finally division is the only arithmetic operator that isn’t defined for all possible values since division by zero has no mathematical meaning. In int division, trying to divide by 0 will result in an ArithmeticException which will crash your program and spit out an error message. With doubles, dividing by 0.0 produces a special value Infinity which is maybe better than crashing but only by a bit in that math breaks down at infinity: Infinity plus or times anything is still Infinity; Infinity minus or divided by anything finite is Infinity, and Infinity minus or divided by Infinity is an even weirder special value NaN which stands for “Not a Number”. However you don’t need to know about these weird double values for the AP Exam.
Java supports one last arithmetic operator that you also probably learned about in elementary school but not as a separate operator. The percent sign (%) is the remainder operator.2 Like the other arithmetic operators is takes two operands. Mathematically it returns the remainder after performing a truncating integer division of the first number by the second. For instance, 5 % 2 evaluates to 1 since 2 goes into 5 two times with a remainder of 1.
While you may not have heard of remainder as an operator, think back to elementary school math. Remember when you first learned long division, before they taught you about decimals, how when you did a long division that didn’t divide evenly, you gave the answer as the number of even divisions and the remainder. That remainder is what is returned by this operator. In the figures below, the remainders are the same values that would be returned by 2 % 3 and 5 % 2.
There are a few idiomatic uses of the remainder operator that come up fairly frequently:
Use it to get the last digit from an integer: num % 10 gives us the rightmost digit of num if num is positive.
Use it to for “clock arithmatic”. If it’s 8:00 now what time will it be in five hours: (8 + 5) % 12 gives us 1 so 1:00.3
Use it to get the number of seconds left after you take out the whole minutes: totalSeconds % 60. (Consider two minutes and thirteen seconds, also known as 133 seconds. 133 % 60 is 13.) This is easier than figuring out the hour because we’re assuming totalSeconds is positive and 0 is an acceptable answer.
Use it to wrap a number around to zero whenever it goes over some limit. This is basically clock arithmetic again but generalized: the value of num %
limit will always be in the range from 0 (inclusive) to limit (exclusive) as long as num and limit are both positive.
One last operator we need to know abut is the cast operator which has no analogue in math class because it has to do solely with how numbers are represented in the computer. Because int and double are different data types, their values are distinct even when they’re mathematically equivalent. But sometimes we’ll have one and want the other. For instance, if we’re computing an average score in a class on a ten-point quiz, the number of students in the class would most reasonably be represented with an int (there are no fractional students) and assuming there’s no partial credit the individual scores are also likely to be recorded is ints. When we add up those ints the total will thus also be an int. But if we divide an int (the total) by an int (the number of students) we get a truncating division which is not what we want.
That’s where the cast operator comes in. It’s not clear where the name “cast” came from in programming but it’s probably rooted in the same idea as casting bronze where molten bronze is reshaped by being “cast” in a mold. The cast operator changes the shape (type) of the value it is applied to.
Unlike the arithmetic operators which operate on the value of two expressions and is written between them, the cast operator operates on only one value and is written before the expression that produces the value. It is written as a pair of parentheses containing the name of the type of value we want, like (int) or (double). For example:
(double) 19 gives us the value 19.0.
(int) 3.14 gives us the value 3.
As you can see, casting an int value to double doesn’t lose any information because all ints have a mathematically equivalent double value. But casting a double value to int loses the fractional part, truncating the number the same way results of int division get truncated.
Thus the two kinds of casts serve different purposes. Casting to an int is mostly useful after you’ve done a complete calculation with double values and want, at the end, to convert it to an integer value. Remember though that truncation is not the same as rounding.4
Casting to a double has a very different effect because of the rule about double contagion. Recall that in an expression involving one int and one double, the int is first automatically converted to a double and then the expression is evaluated using the rules of double math. Because of this rule, casting one int value in an arithmetic expression to a double is enough to force the result to also be a double. Thus (double) 19 / 10 gives us 1.9 rather than 1 because it is equivalent to 19.0 / 10 which is equivalent to 19.0 / 10.0.
Obviously when we’re dealing with literal values it’s kind of silly to use casts since if we wanted the value 19.0 we could just write that rather than casting 19 to a double. But the cast operator can be used on any numeric expression to convert from one type of number to the other.
Note that the precedence of the cast operator higher than all the arithmetic operators (but lower than parentheses) so (double) 19 / 10 is equivalent to ((double) 19) / 10, i.e. the cast happens first and then the division.
Because of the high precedence of the cast operator, when we want to cast the result of a double expression to int we need to explicitly parenthesize the expression whose value we want to cast. To see why, consider these two expressions:
(int) (0.956 * 100.0)
(int) 0.956 * 100.0
The first expression computes the double result of the multiplication giving us 95.6 and the cast then truncates that down to the int 95. The second expression, however, gets evaluated in a slightly more convoluted way. First the cast converts 0.956 by truncation to the int 0. Then because of double contagion that int value is converted back to a double before performing the multiplication. But the fractional part has been lost so we get 0.0 * 100.0 resulting in the double value 0.0. Sometimes that might be what you want but most often when casting to int you’ll want to use parentheses to group the whole expression whose value you want to cast.
Next: Booleans
1 There is no exponentiation operator in Java. You may be used to using ^ for exponentiation, either from a graphing calculator or tools like Desmos. Confusingly ^ is an operator in Java but it has a completely different meaning than exponentiation and isn’t even exactly an arithmetic operator. Later we’ll learn about other ways to do more complex mathematical computations than are supported directly via numeric operators.
2 Sometimes people will call % the modulo, or mod, operator. That is not actually correct though the difference between remainder and modulo only matters when the signs of the operands differ, so usually it doesn’t matter. Having % mean remainder is quite common in programming languages. In some languages, however, % actually is modulo. To compute a modulo b in Java, or any other language where % is remainder, you need to write ((a % b) + b) % b or, better yet, use the method Math.floorMod from the Math class which we’ll learn about later. But on the AP exam, all the uses of % will be ones where this distinction doesn’t matter.
3 Note, however, that this formula will return 0 for 12:00. To translate 0 to 12 is a tiny bit more involved: If h is any positive number of hours after midnight, e.g. 103 hours, then this formula will give the number of the hour on a 12-hour clock: 1 + (h + 11) % 12. (If h can be negative it’s a bit more of a pain: ((h - 1) % 12 + 12) % 12 + 1. Feel free to puzzle out why that works and why all the parts are needed. Or feel free to skip it.
4 You can use a cast to int as part of an expression to round a double within the range representable by ints to the nearest int by adding or subtracting 0.5 before casting.
For example, if we divide 7.0 / 4.0 we get 1.75. If we cast that to an int, it will be truncated to 1. However if add 0.5 when the fractional part is greater than 0.5, as it is here, the result will be greater than the next int value and casting that value to an int will truncate down. So in this case 1.75 + 0.5 gives us 2.25 which is then truncated to 2, the correctly rounded result. On the other hand adding 0.5 to the result of evaluating 5.0 / 4.2, namely 1.25, only gets us to 1.75 which truncates back to 1 which is also correctly rounded.