Lesson 1.5 · Unit 1 · CED topic 1.5
Casting and the range of variables
Java keeps int and double apart on purpose, but real programs mix them all the time: an average of whole numbers, a price rounded to cents, a percentage of a count. This lesson covers the tool that converts between them, the cast, and the two ways numbers can quietly go wrong: values that fall out of an int's range and values a double cannot hold exactly.
Learning objectives.
- 1.5.A. Develop code to cast primitive values to different primitive types in arithmetic expressions and determine the value that is produced as a result.
- 1.5.B. Describe conditions when an integer expression evaluates to a value out of range.
- 1.5.C. Describe conditions that limit accuracy of expressions.
Essential knowledge.
- 1.5.A.1. The casting operators (int) and (double) can be used to convert from a double value to an int value (or vice versa).
- 1.5.A.2. Casting a double value to an int value causes the digits to the right of the decimal point to be truncated.
- 1.5.A.3. Some code causes int values to be automatically cast (widened) to double values.
- 1.5.A.4. Values of type double can be rounded to the nearest integer by (int) (x + 0.5) for non-negative numbers or (int)(x - 0.5) for negative numbers.
- 1.5.B.1. The constant Integer.MAX_VALUE holds the value of the largest possible int value. The constant Integer.MIN_VALUE holds the value of the smallest possible int value.
- 1.5.B.2. Integer values in Java are represented by values of type int, which are stored using a finite amount (4 bytes) of memory. Therefore, an int value must be in the range from Integer.MIN_VALUE to Integer.MAX_VALUE inclusive.
- 1.5.B.3. If an expression would evaluate to an int value outside of the allowed range, an integer overflow occurs. The result is an int value in the allowed range but not necessarily the value expected.
- 1.5.C.1. Computers allot a specified amount of memory to store data based on the data type. If an expression would evaluate to a double that is more precise than can be stored in the allotted amount of memory, a round-off error occurs. The result will be rounded to the representable value. To avoid rounding errors that naturally occur, use int values.
Outside the course: Other special decimal data types that can be used to avoid rounding errors are outside the scope of the AP Computer Science A course and exam.
Casting with (int) and (double)
A casting operator converts a value to another type. The two you need are (int) and (double), written in parentheses directly before the value.
(int)on adoubletruncates: the digits to the right of the decimal point are dropped, with no rounding.(int) 3.99is3, and(int) -3.99is-3, because truncation always moves toward zero.(double)on anintproduces the same number with a decimal part:(double) 4is4.0.- Some code casts automatically. An
intused where adoubleis needed is widened to adoublewithout a cast, which is whydouble x = 7;is legal and stores7.0.
System.out.println((int) 3.99); System.out.println((int) -3.99); System.out.println((double) 4); double widened = 7; System.out.println(widened);
3 -3 4.0 7.0
### A cast binds tightly
A cast applies to the value immediately after it, before any arithmetic operator runs. That makes the position of the parentheses matter.
| Expression | Value | Why |
|---|---|---|
(int) 3.7 * 2 | 6 | the cast happens first: 3 * 2 |
(int) (3.7 * 2) | 7 | multiply first (7.4), then truncate |
(double) 7 / 2 | 3.5 | the cast turns 7 into 7.0 first, so real division |
(double) (7 / 2) | 3.0 | integer division first (3), then widen |
The second pair is the most useful pattern in the course. To compute an average of int values without losing the fraction, cast one operand before the division:
int sum = 17; int count = 4; System.out.println(sum / count); System.out.println((double) sum / count); System.out.println((double) (sum / count));
4 4.25 4.0
### Rounding to the nearest integer
A cast truncates, so to round you first nudge the value. For a non-negative x, (int) (x + 0.5) gives the nearest integer. For a negative x, (int) (x - 0.5) does. To round to a number of decimal places, scale up, round, and scale back down:
double price = 12.3456; int nearestDollar = (int) (price + 0.5); double nearestCent = (int) (price * 100 + 0.5) / 100.0; System.out.println(nearestDollar); System.out.println(nearestCent);
12 12.35
The range of an int
Integers in Java are stored in a fixed amount of memory (4 bytes), so an int can only hold values between two limits. Two constants name them.
| Constant | Value |
|---|---|
Integer.MAX_VALUE | 2147483647 |
Integer.MIN_VALUE | -2147483648 |
If an expression would produce an int outside that range, integer overflow occurs. Java does not stop and does not warn. The result is an int that is in the allowed range but not the value you expected, because the value wraps around.
int big = Integer.MAX_VALUE; System.out.println(big); System.out.println(big + 1);
2147483647 -2147483648
When a double is not exact
A double also lives in a fixed amount of memory. Many decimal numbers, such as 0.1, have no exact binary representation, so the stored value is the nearest one that fits. Arithmetic on these values can produce a round-off error.
System.out.println(0.1 + 0.2); System.out.println(0.1 + 0.2 == 0.3);
0.30000000000000004 false
The rule that follows: do not rely on exact equality of computed double values, and when exactness matters, use int values. A program that tracks money in whole cents with an int never accumulates this error.
Give the value and the type of each expression.
int a = 9; int b = 4; double d = 2.5; System.out.println(a / b); System.out.println((double) a / b); System.out.println((int) d * 3); System.out.println((int) (d * 3)); System.out.println((int) (d + 0.5));
2 2.25 6 7 3
| Expression | Reasoning | Value |
|---|---|---|
a / b | both int, so integer division | 2 (an int) |
(double) a / b | cast first, then 9.0 / 4 | 2.25 (a double) |
(int) d * 3 | (int) 2.5 is 2, times 3 | 6 (an int) |
(int) (d * 3) | 7.5 truncated | 7 (an int) |
(int) (d + 0.5) | 3.0 truncated | 3 (an int) |
A program computes the average of three test scores and displays 88.0, though the real average is 88.33.
int s1 = 90; int s2 = 85; int s3 = 90; double wrong = (s1 + s2 + s3) / 3; double right = (s1 + s2 + s3) / 3.0; System.out.println(wrong); System.out.println(right);
88.0 88.33333333333333
The sum is 265, an int, and 265 / 3 is integer division: it throws away .33 before the result is ever stored in the double. The fix is to make one operand a double. Writing 3.0 does it, and so does (double) (s1 + s2 + s3) / 3.
A program totals items that cost 1.10 and 2.20 dollars, then repeats the calculation in whole cents.
double dollars = 1.10 + 2.20; int cents = 110 + 220; System.out.println(dollars); System.out.println(cents / 100 + " dollars and " + cents % 100 + " cents");
3.3000000000000003 3 dollars and 30 cents
The double total is slightly off, a round-off error, because neither 1.10 nor 2.20 is stored exactly. The int version is exact for every input, because whole numbers within the int range are stored exactly. Use / and % to split cents into dollars and cents.
Expect a question that asks for the value of an expression with casts in different positions, and one that asks what happens when a value exceeds Integer.MAX_VALUE (an in-range but wrong value, never an error message). Also expect the average pattern: the exam wants you to notice that the cast must come before the division. Know the rounding formula (int) (x + 0.5) for non-negative values. Other special decimal types that avoid round-off error are outside the course.
- What is printed?
double x = 8.9; int y = (int) x; System.out.println(y); System.out.println((int) -x);
Show answer
8and-8. The cast truncates toward zero, so 8.9 becomes 8 and -8.9 becomes -8. It does not round. - What is printed?
int total = 7; int n = 2; System.out.println(total / n); System.out.println((double) total / n); System.out.println((double) (total / n));
Show answer
3,3.5,3.0. The first is integer division. In the second the cast makestotaladoublebefore dividing. In the third the division is already finished (giving 3) before the cast widens it. - Complete the statement so that
pctholds the percent of questions answered correctly as adouble(for 17 of 20 it should hold 85.0).int correct = 17; int total = 20; double pct = 100.0 * correct / total; System.out.println(pct);
Show answer
100.0 * correct / totalmultiplies first (left to right) with adouble, giving 1700.0, then divides by 20. Writing100 * correct / totalwould have used integer arithmetic throughout, which gives 85 here but would give 62 for 5 correct out of 8 instead of 62.5. - A student claims the code below throws an exception. Explain what really happens.
int n = Integer.MAX_VALUE; n = n + 1; System.out.println(n);
Show answer
No exception occurs. The sum is outside the
intrange, so integer overflow produces a differentintin the allowed range. Here it wraps toInteger.MIN_VALUE, which is -2147483648. - Write an expression that rounds the non-negative
doublevariablegradeto the nearest integer.double grade = 86.5; int rounded = (int) (grade + 0.5); System.out.println(rounded);
Show answer
(int) (grade + 0.5). Adding 0.5 pushes any fractional part of 0.5 or more up to the next whole number, and the cast then truncates, so 86.5 becomes 87. - Why is
0.1 + 0.2 == 0.3false, and how can a program avoid this kind of surprise when counting money?Show answer
A
doublestores the nearest representable value, and 0.1 and 0.2 are not stored exactly, so their sum differs slightly from the stored 0.3 (a round-off error). Storing money in whole cents in anintavoids it, because integers in range are exact.