Values and expressions

Booleans

Numbers

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.

Literal numeric values

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:

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.

Arithmetic expressions

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:

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.

Division

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:

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.

Remainder

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:

The cast operator

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:

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.