The average that lost its decimals
Riya's five subject marks add up to 437. She writes double avg = total / 5; expecting 87.4. The program prints 87.0. She changed the variable to a double, so where did the .4 go?
Sir, avg is a double, so why did I get 87.0 and not 87.4?
Because Java calculated total / 5 first, and both of those are ints. int divided by int gives an int: 87. Only then was 87 widened into the double box as 87.0. The .4 was lost before the double ever saw it.
So I need to make it a double before dividing?
Exactly: (double) total / 5. That's a cast. Today you'll learn which conversions Java does for you, which ones need a cast, and what you can lose.
What is type casting?
Type casting (type conversion) is converting a value from one data type to another. A widening (implicit) conversion goes from a smaller to a larger type and is done automatically by the compiler, e.g. int to double. A narrowing (explicit) conversion goes from a larger to a smaller type and requires a cast operator (type), e.g. (int) 87.4; it may lose information through truncation or overflow.
Small box → big box: Java does it for you, nothing spills. Big box → small box: you must write the cast to say “I know, do it anyway”, and anything that doesn't fit is cut off.
Words you'll meet in this lesson
(type) written before a value: (int) price.(int) 9.99 is 9, not 10.Integer.parseInt("42"). Not a cast.The widening ladder
| Automatic direction | byte → short → int → long → float → double, and char → int |
|---|---|
| Needs a cast | Any step down the ladder, e.g. double → int, int → byte, int → char |
| Never allowed | boolean ↔ any number. (int) true doesn't compile. |
| double → int | Decimal part is dropped (towards zero): (int) 9.99 = 9, (int) -9.99 = -9 |
| int → byte | Only the lowest 8 bits are kept: (byte) 300 = 44 |
| Expression type | The result has the widest type involved, at least int: int / int = int, int / double = double |
Exam tip: “widening = implicit = safe”, “narrowing = explicit = may lose data”. One trap: long → float is widening and automatic, but can still lose precision, because float keeps only about 7 significant digits.
Kinds of conversions
Widening (implicit)
int n = 42; double d = n;
Automatic, no data lost (except the long→float precision case).
Narrowing (explicit)
double d = 87.4; int n = (int) d;
You must write the cast. Decimals are cut; big values wrap.
Promotion in expressions
byte a = 10, b = 20;a + b is an int. Mixing int and double gives a double.
Compound assignment
byte b = 10; b += 300;
Compiles: += includes a hidden narrowing cast. Result: 54.
Casting vs converting between text and numbers
| Goal | Use | Example |
|---|---|---|
| double → int (cut decimals) | cast | (int) 87.4 → 87 |
| double → nearest whole number | Math.round | Math.round(87.6) → 88 (a long) |
| char → its code | cast or widening | (int) 'A' → 65 |
| Text → number | parse | Integer.parseInt("42"), Double.parseDouble("19.5") |
| Number → text | String.valueOf or + "" | String.valueOf(42) → "42" |
You can't cast between String and numbers: (int) "42" is a compile error. Strings are objects, not numbers. Use Integer.parseInt, which throws NumberFormatException if the text isn't a valid number.
How Java decides the type of an expression
Look at each operand's type
In
javac works this out at compile time and chooses the bytecode instruction:total / 5,totalis int and5is an int literal.idivfor ints,ddivfor doubles.Promote small types to int
byte, short and char become int before any arithmetic.
The JVM has no byte-addition instruction at all. Small values are loaded as ints, which is whybyte + byteis an int.Widen to the biggest type present
If either side is long, float or double, the other side is widened to match.
For(double) total / 5, the cast happens first (it binds tighter than/), so 437.0 / 5 usesddivand gives 87.4.Do the operation in that type
int ÷ int = int (fraction dropped). double ÷ int = double.
The result's type is fixed. Assigning an int result to a double variable widens it afterwards; the lost fraction can't come back.Assign, widening automatically or casting explicitly
Storing into a bigger type is automatic. Storing into a smaller one needs a cast, or javac reports “possible lossy conversion”.
A narrowing cast compiles to instructions liked2i(double to int) ori2b(int to byte), which drop bits or decimals.
What a cast does to the bits: both views
Left: the code and what prints. Right: the value inside memory before and after.
- Widening: an int goes into a double automatically. 42 becomes 42.0.
- Narrowing:
(int) 87.4gives 87. The decimals are truncated, not rounded. (byte) 300prints 44. A byte can't hold 300, so the value is chopped.- Riya's bug:
total / 5is int division, giving 87, which is then widened to 87.0. - Casting
totalto double before dividing gives the right answer, 87.4. a + bwith two bytes is an int, so storing it in a byte needs a cast.
- javac inserts an
i2dinstruction. Every int fits exactly in a double. d2idrops the fraction, rounding towards zero.i2bkeeps only the lowest 8 bits of 300:0010 1100= 44.- Both operands are ints, so javac uses
idiv. The fraction is gone before thei2dwidening. - With the cast, 437 becomes 437.0, 5 is widened, and
ddivkeeps the .4. - byte values are promoted to int for arithmetic, so storing the result back needs a cast.
Where conversions bite in real systems
Conversions are everywhere data crosses a boundary: from the database, the network, a file or a calculation.
Watch it with an example: a product's average rating
Three customers rate a phone cover 5, 4 and 5 stars. Watch how integer division loses the fraction, and how one cast fixes it.
- Data: the ratings add up to
sum = 14, andcount = 3. Both areint. - Bug:
sum / countdivides two ints, so Java does integer division and drops the fraction: 4. Storing it in a double later only makes it 4.0. - Cast:
(double) sumwidens 14 to 14.0 before dividing. Now one side is double, so the other is widened too. - Exact-ish result: 4.666666666666667. The fraction is kept.
- Display: round to one decimal for the screen: ★ 4.7. Shopping and food apps do exactly this for their ratings.
1. Average ratings and percentages
Ratings, attendance percentages and averages all divide one count by another. Using doubles (or casting first) avoids showing 4.0 when the real average is 4.3.
2. Rounding money for display or payment
Payment gateways often need amounts in paise as whole numbers. Converting with a plain cast truncates; systems use Math.round or BigDecimal with an explicit rounding mode.
3. Reading numbers from text
Form inputs, CSV files and query parameters arrive as text. They're parsed, not cast, and invalid text must be handled (Module 9).
4. Binary data from devices and files
Bytes read from a sensor, image or network packet are Java bytes (−128 to 127). To get the unsigned value 0–255, code converts with b & 0xFF.
5. Sizes and counts from big systems
File sizes, row counts and timestamps are usually long. Casting them to int to pass into an older API works until the value passes 2.1 billion, then silently goes wrong. Math.toIntExact throws an exception instead.
Examples are simplified to show where conversions happen. Real systems vary.
Riya's marks, converted correctly
public class Main {
public static void main(String[] args) {
int total = 437;
double wrongAvg = total / 5; // int division first
double avg = (double) total / 5; // cast first
int whole = (int) avg; // truncates
long rounded = Math.round(avg); // rounds
byte squeezed = (byte) 300; // keeps lowest 8 bits
int parsed = Integer.parseInt("42"); // text to number
System.out.println(wrongAvg + " vs " + avg);
System.out.println(whole + " " + rounded + " " + squeezed + " " + parsed);
}
}total / 5int ÷ int = int (87). Widened to 87.0 only afterwards.(double) total / 5The cast applies tototalfirst, so the division is done in double.(int) avgNarrowing: drops .4. It never rounds up.Math.round(avg)Rounds to the nearest whole number and returns along.(byte) 300Only the lowest 8 bits survive: 44.Integer.parseInt("42")Text to number is parsing, not casting.
Every conversion, step by step
Watch each value change type. The note shows the bytecode conversion instruction where one happens.
Worked examples: real problems, solved and traced in memory
Casting changes a value from one type to another. Widening (int → double) is automatic and safe; narrowing (double → int, long → int) needs an explicit cast and can lose data. These programs show both, with real business numbers.
Average rating: integer division vs casting
Problem statement
A restaurant has 3 reviews with total stars 13. Compute the average rating. Dividing two ints gives 4, not 4.33. Cast to double first to keep the fraction, then show the rating as whole stars by casting back to int.
total stars 13, reviews 3
int division: 4 average: 4.333333333333333 whole stars shown: 4
Approach
13 / 3is integer division: 4.(double) 13 / 3widens first: 4.333….(int) avgnarrows: the fraction is cut off (not rounded).
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Squeezing a long into an int loses data
Problem statement
An old reporting system stores amounts in int. A large transfer of Rs 3,000,000,000 (in a long) is cast to int. Show the corrupted value and how to detect the problem with Math.toIntExact.
amount 3000000000
long: 3000000000, cast to int: -1294967296 refused: integer overflow
Approach
- Cast
(int) amount: the extra bits are dropped silently. Math.toIntExact(amount)throws instead of losing data.- Catch it and report the problem.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
From seat letter to row number and back
Problem statement
A bus booking app labels rows 'A' to 'L' but stores them as numbers 1–12. Convert 'H' to its row number and convert row 3 back to a letter, using char ↔ int casts.
seat row 'H'; row number 3
Row H is number 8 Number 3 is row C
Approach
row - 'A' + 1: char arithmetic gives an int.(char) ('A' + n - 1)turns a number back into a letter.- Print both conversions.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Check yourself
Quick revision
Lesson 2.4 in one screen
- Widening (automatic):
byte → short → int → long → float → double,char → int. - Narrowing (needs
(type)): may truncate decimals or keep only the low bits. (int) 9.99 = 9,(int) -9.99 = -9. UseMath.roundto round.- Arithmetic promotes byte/short/char to int; the result type is the widest involved.
int / int= int. Cast before dividing:(double) a / b.+=,-=… include a hidden cast.- Text ↔ number is parsing/formatting, not casting. boolean never converts.
Interview and viva questions
What is the difference between implicit and explicit casting?
Implicit (widening) casting is done automatically by the compiler when converting to a type that can hold all values, like int to long. Explicit (narrowing) casting must be written with a cast operator when converting to a smaller or less precise type, like double to int, and may lose data.
Why does byte b = 10; compile if 10 is an int?
Because 10 is a compile-time constant that fits in a byte, Java allows the implicit narrowing for constants. byte b = x; where x is an int variable would not compile.
Why is long → float called widening if it can lose precision?
Because float's range is larger than long's (up to about 3.4 × 1038), so no value overflows. But float has only about 7 significant digits, so large longs are rounded. Java classifies it as widening but warns in the specification about precision loss.
What happens when you cast a very large double to int?
For double to int, Java uses saturation: values above Integer.MAX_VALUE become MAX_VALUE, values below MIN_VALUE become MIN_VALUE, and NaN becomes 0. For int to byte, the bits are chopped instead, so the result can wrap around.
Does s += 1 work when s is a short?
Yes. Compound assignment operators include an implicit cast back to the variable's type, so s += 1 compiles while s = s + 1 doesn't.
Practice problems
Widen automatically, narrow with care. Predict each output before running. The last three are business tasks.
Automatic widening
Assign an int 50 to a double and print it.
50
50.0
Show solution and explanation
public class Main {
public static void main(String[] args) {
int i = 50;
double d = i;
System.out.println(d);
}
}How it works: int fits into double without loss, so no cast is needed; it prints 50.0.
Truncation
Cast 9.99 to int and print it.
9.99
9
Show solution and explanation
public class Main {
public static void main(String[] args) {
System.out.println((int) 9.99);
}
}How it works: casting to int drops the fraction; it does not round.
char to int
Print the int value of '0' and of '9'.
'0', '9'
48 57
Show solution and explanation
public class Main {
public static void main(String[] args) {
System.out.println((int) '0' + " " + (int) '9');
}
}How it works: digit characters have codes 48 to 57, not 0 to 9.
Digit character to number
Convert the character '7' to the number 7 and add 1.
'7'
8
Show solution and explanation
public class Main {
public static void main(String[] args) {
char c = '7';
int digit = c - '0';
System.out.println(digit + 1);
}
}How it works: subtracting '0' (48) turns a digit character into its value.
Rounding vs casting
For 7.6, print the cast to int and Math.round.
7.6
7 8
Show solution and explanation
public class Main {
public static void main(String[] args) {
double x = 7.6;
System.out.println((int) x + " " + Math.round(x));
}
}How it works: casting cuts to 7; rounding gives 8.
Percentage of target
Sales were 37 out of a target of 40. Print the percentage with decimals.
37, 40
Achieved 92.5%
Show solution and explanation
public class Main {
public static void main(String[] args) {
int sold = 37, target = 40;
double pct = (double) sold / target * 100;
System.out.println("Achieved " + pct + "%");
}
}How it works: without the cast, 37 / 40 would be 0 and the answer 0.0%.
Whole cartons needed
A shop must ship 250 bottles, 24 per carton. Print cartons needed (round up).
250, 24
Cartons: 11
Show solution and explanation
public class Main {
public static void main(String[] args) {
int cartons = (int) Math.ceil(250 / 24.0);
System.out.println("Cartons: " + cartons);
}
}How it works: Math.ceil rounds up (10.41 → 11.0); the cast turns the double into an int.
Price to paise
Convert Rs 129.95 to paise as a long, once with a plain cast and once with rounding.
129.95
cast: 12994 paise, rounded: 12995 paise
Show solution and explanation
public class Main {
public static void main(String[] args) {
double rupees = 129.95;
long cast = (long) (rupees * 100);
long rounded = Math.round(rupees * 100);
System.out.println("cast: " + cast + " paise, rounded: " + rounded + " paise");
}
}How it works: 129.95 × 100 is 12994.999999999998 in binary floating point, so the cast cuts it to 12994, one paisa short. Rounding gives the correct 12995. Always round when converting money.
Real-world scenario questions (asked in company interviews)
Conversion bugs don't crash; they quietly produce wrong numbers. Decide your answer first, then open the model answer.
Every restaurant shows a whole-number rating
A restaurant listing page shows ratings like 4.0 and 3.0 but never 4.3 or 3.7. The code is double rating = totalStars / reviewCount;, where both are ints read from the database. What's wrong and how would you fix it?
The interviewer is checking: do you spot integer division and know where the cast must go?
Show a strong answer
Both operands are ints, so Java does integer division and drops the fraction before the result is widened into the double.
- Fix:
double rating = (double) totalStars / reviewCount;, casting before dividing. - Guard against
reviewCount == 0(no reviews yet), which would otherwise give an error or NaN. - Round only for display, e.g. to one decimal place, and add a unit test with an average like 4.33.
Avoid: (double) (totalStars / reviewCount). The cast is too late; the division has already dropped the fraction.
Customers are charged one paisa less
Before calling the payment gateway, the code converts a rupee amount to paise with long paise = (long) (amount * 100);. For ₹19.99, the gateway receives 1998 paise. Why?
The interviewer is checking: do you understand that floating-point results can be slightly below the expected value, and that casts truncate?
Show a strong answer
19.99 can't be stored exactly as a double. 19.99 * 100 comes out as something like 1998.9999999999998, and the cast truncates it to 1998.
- Short-term: use
Math.round(amount * 100)instead of a cast. - Proper fix: never hold money in double. Keep amounts in paise (long) or BigDecimal from the moment they enter the system.
- Add tests with known problem values such as 19.99, 0.29 and 1.15.
Avoid saying: “The gateway has a bug.” The truncation happened in our code.
Large uploads show a negative size
Users uploading files larger than about 2 GB see a negative file size, and the progress bar breaks. The upload code reads long size = file.length(); and passes (int) size to an older progress-tracking method. Explain and fix.
The interviewer is checking: do you know what a narrowing cast does to values beyond the target range?
Show a strong answer
An int holds at most 2,147,483,647 bytes (about 2 GB). Casting a larger long to int keeps only the low 32 bits, which can be read as a negative number. The cast hid the problem from the compiler.
- Change the progress method to accept a
long. - If an int is truly required, use
Math.toIntExact(size), which throws an exception instead of producing a wrong value, and handle that case. - Search the codebase for other
(int)casts on sizes, counts and timestamps.
Avoid: adding casts just to make the compiler quiet. A cast says “I accept data loss”.
Negative humidity readings
A device sends humidity as one byte from 0 to 255. Readings above 127 show up in the Java dashboard as negative numbers, like −56 for a real value of 200. What's happening?
The interviewer is checking: do you know that Java's byte is signed, and how to read unsigned values?
Show a strong answer
Java's byte is signed: −128 to 127. The bit pattern for 200 (1100 1000) has the top bit set, so Java reads it as −56.
- Convert to an unsigned int:
int humidity = b & 0xFF;(orByte.toUnsignedInt(b)). This keeps the 8 bits and treats them as 0–255. - Document the byte format of the device protocol, and add tests with values above 127.
Avoid: adding 256 to negative values in scattered places. Convert once, where the bytes are read.