Casting & Ranges of Values
Widening vs. Narrowing
Java moves values between numeric types two different ways:
- Widening — a smaller type flows into a bigger one automatically, no cast needed:
intintodoubleis always safe (no information is lost), sodouble d = 5;just compiles. - Narrowing — a bigger type into a smaller one loses information, so Java requires an explicit cast to make sure you meant it:
doubleintointneeds(int). This is exactly the rule behind the "possible lossy conversion" compile errors from 2.1 and 2.3.
Converting Between Types (Casting)
Put (int) or (double) right before a value to convert it to that type on the spot:
(double) 1 / 3 // 0.333... — the 1 becomes a double first, then divides
(int) 3.6 // 3 — cuts off everything after the decimal point
(int) always cuts off the decimal — it never rounds. (int) 3.9 is 3, not 4.
If you actually want to round, add 0.5 first, then cast:
int rounded = (int)(5.7 + 0.5); // 6 — this is rounding, not truncating
This add-0.5 trick only rounds correctly for non-negative numbers. For a negative value it rounds the wrong way: (int)(-5.7 + 0.5) is -5, not -6 — and FRC values (joystick input, encoder deltas) are often negative. Java's own Math.round(...) (Ch.4) handles the sign correctly and is the better tool for real code; this trick is worth knowing because you'll see it, and because it explains why Math.round exists.
The Fix for Truncating Division
Lesson 2.2 showed that dividing two whole numbers cuts off the decimal. Casting one of them fixes it — but only if the cast happens before the division:
int total = 17;
int games = 5;
double average = (double) total / games; // correct: 3.4
double wrong = (double) (total / games); // wrong: int division first, then cast: 3.0
Whole Numbers Have a Limit
An int can only hold values in a fixed range (roughly ±2.1 billion). Go past that limit, and the number silently wraps around to a wrong value — no error, no warning.
System.out.println(Integer.MAX_VALUE + 1); // wraps to the smallest possible int
(Integer here is a built-in helper class that travels alongside the int primitive, and MAX_VALUE is a constant it defines — both get a proper introduction in 9.2.)
This is unlikely to come up in a match, but it's a real risk for a counter that keeps growing and is never reset.
Decimals Have a Precision Limit Too
A double can only store so many significant digits (about 15-16). Beyond that, the extra digits get quietly rounded off. The everyday cause isn't running out of digits, though — most decimals simply can't be stored exactly in binary at all, the same way 1/3 can't be written exactly in decimal:
System.out.println(0.1 + 0.2); // 0.30000000000000004, not 0.3
System.out.println(0.1 + 0.2 == 0.3); // false
This is why comparing two doubles with == is risky — two values that "should" be mathematically equal can differ by a tiny rounding amount you never typed in yourself. The fix, needed constantly for things like checking whether a robot arm reached its target angle, is a tolerance comparison: instead of asking "are these exactly equal," ask "are these close enough":
double target = 45.0;
double current = 44.98;
double tolerance = 0.05;
boolean closeEnough = Math.abs(current - target) < tolerance; // true
Math.abs(...) (Ch.4) strips the sign, so it doesn't matter whether current is a bit above or a bit below target — only how far apart they are.
Common Pitfalls
- Casting after dividing instead of before. The division already happened as whole numbers by the time the cast runs — putting the cast in the wrong spot doesn't fix anything.
- Assuming a cast rounds.
(int)cuts off the decimal. Rounding needs the add-0.5-then-cast trick — and even that trick only works for non-negative numbers. - Comparing
doubles with==. Two values that "should" be equal often aren't, bit-for-bit — use a tolerance comparison (Math.abs(a - b) < tolerance) instead.
Key Takeaways
- Widening (small type into big, e.g.
intintodouble) happens automatically; narrowing (big into small, e.g.doubleintoint) needs an explicit cast. (int)and(double)convert a value's type on the spot;(int)always cuts off decimals, never rounds.- To fix truncating division, cast one of the numbers before dividing — position matters.
intvalues have a fixed range; going past it silently wraps to a wrong value.doublevalues have a precision limit and can't store most decimals exactly — compare with a tolerance (Math.abs(a - b) < tolerance) instead of==.
Derived from csawesome-2026: unit-1-using-objects-and-methods/1.05-casting-and-ranges-of-values.md (CSA 1.5)