skip to content

Primitive Widening & Narrowing

Widening happens implicitly because no information is lost; narrowing requires an explicit cast and can truncate or wrap silently. Interviewers use a cast from int to byte to see whether you can predict the resulting value.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What is a widening primitive conversion in Java, and why does it happen automatically?

level: juniorimportance: must knowfreq 62%

answer

  1. Smaller range -> bigger range, no cast
  2. byte short int long float double; char into int and up
  3. Magnitude always safe; exactness not (int/long -> float/double)
  4. char is unsigned, widens to int+

basics

~20 s

Widening turns a smaller numeric type into a bigger one, like int to long or int to double. Java does it automatically because the bigger type can always hold the value, so no data is lost.

solid answer

~40 s

A widening primitive conversion converts a value to a type with a larger (or at least equal) range, such as int to long, long to float, or int to double. Java performs it implicitly (no cast needed) because the target type can represent every value of the source type, so the conversion is considered safe. The defined widening paths are byte to short to int to long to float to double, plus char to int and up. There is one subtlety: int to float and long to float/double can lose precision because floating-point types have limited significant bits, even though magnitude is preserved. Magnitude is never lost, but exactness can be for very large integer values converted to float or double.

go deeper

for a junior

Knows widening goes small-to-big and is automatic, and can name examples like int to long and int to double.

for a middle

Knows the full widening order, that char widens to int, and that int/long to float/double can lose precision despite being widening.

for a senior

Can explain the mantissa/precision reason behind floating-point precision loss and reason about exact bit counts (24 vs 32, 53 vs 64).

for a principal

Frames it in terms of JLS-defined conversion contexts and can discuss API design implications (e.g. avoiding silent precision loss in numeric pipelines).

## What "primitive" and "conversion" mean Java has eight **primitive types** built into the language: `byte`, `short`, `int`, `long`, `float`, `double`, `char`, and `boolean`. The first six are numeric. Each numeric type has a fixed size in memory and therefore a fixed **range** of values it can hold: | Type | Size (bits) | Approx. range | |------|------|------| | `byte` | 8 | -128 to 127 | | `short` | 16 | -32,768 to 32,767 | | `char` | 16 | 0 to 65,535 (unsigned) | | `int` | 32 | about +/- 2.1 billion | | `long` | 64 | about +/- 9.2 quintillion | | `float` | 32 | huge range, ~7 decimal digits of precision | | `double` | 64 | huge range, ~15 decimal digits of precision | A **conversion** means taking a value of one type and producing an equivalent value of another type. ## What widening is A **widening primitive conversion** moves a value from a type with a *smaller* range to a type with a *larger or equal* range. The Java Language Specification defines exactly these widening paths: - `byte` -> `short` -> `int` -> `long` -> `float` -> `double` - `char` -> `int` -> `long` -> `float` -> `double` Because the destination can hold everything the source can hold, the compiler lets you do this **implicitly** -- you do **not** write a cast. Example: ```java int i = 100; long l = i; // int -> long, automatic double d = l; // long -> double, automatic ``` ## Why it is automatic / safe "Safe" here means **the magnitude of the number is always preserved** -- you never get a wildly wrong value. Since the target range fully contains the source range, the number always fits. ## The one caveat: precision vs. magnitude There is an important subtlety. `float` and `double` store numbers as a sign, a fraction (mantissa), and an exponent -- they trade exactness for range. A `float` has only about 24 bits of precision, and a `double` about 53 bits. So: - `int` -> `float`: an `int` has 32 bits of precision, but `float` has only ~24. A large `int` can lose its least-significant digits. - `long` -> `float` and `long` -> `double`: a `long` has 64 bits, more than either floating type's precision, so big `long` values can become approximate. ```java int big = 123_456_789; float f = big; // widening, but... System.out.println((int) f); // 123456792 -- not equal! precision lost ``` So widening **never changes the magnitude** (the value stays roughly the same size), but for these integer-to-floating cases it **can lose precision** (exactness). This is the only data-loss possible in widening, and the language permits it implicitly anyway. ## Special note on char `char` is unsigned (0 to 65,535). It widens to `int` and above by taking its numeric code-point value. It does **not** widen to `byte` or `short`, because those are signed and smaller -- that direction would be narrowing.

  • Does int to float ever lose data, even though it is a widening conversion?
    Yes. float has only ~24 bits of precision while int has 32, so large int values can lose their lowest digits. Magnitude is preserved but exactness is not.
  • Why does char widen to int but not to short?
    char is 16-bit unsigned (0..65535); short is 16-bit signed (max 32767). short cannot hold the upper half of char's range, so that direction is narrowing, not widening. int can hold all char values, so char->int is widening.

saying these in an interview costs you the question

  • Claiming widening can never lose any information (int/long to float/double can lose precision)
  • Thinking a cast is needed for widening
  • Believing char widens to byte or short

context

open as a page

What is a narrowing primitive conversion, why does it require an explicit cast, and what can go wrong?

level: middleimportance: must knowfreq 70%

basics

~20 s

Narrowing converts a bigger type into a smaller one, like double to int or int to byte. Java does not do it automatically because the value might not fit, so you must write a cast like (int)x, and you can lose data.

open as a page

Walk through exactly how Java computes (byte) 257 and (short) 70000, and explain the underlying mechanism.

level: middleimportance: should knowfreq 40%

basics

~20 s

Java keeps only the low bits that fit the smaller type and throws the rest away. (byte) 257 is 1 because 257 is 256+1 and only the low 8 bits survive. (short) 70000 is 4464 for the same reason with 16 bits.

open as a page

How do widening rules apply inside arithmetic expressions (numeric promotion), and what surprises does that cause?

level: seniorimportance: should knowfreq 48%

basics

~20 s

In math expressions Java automatically widens small types to at least int before computing. So byte + byte gives an int, and mixing an int with a double makes the whole thing a double. This is why byte b = b1 + b2; needs a cast.

open as a page

As a library designer, how do you prevent silent precision loss and overflow from primitive conversions, and which JLS conversion contexts are involved?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Avoid letting numbers silently shrink or lose digits. Use long instead of int where overflow is possible, BigDecimal for exact money math, and helpers like Math.toIntExact that throw on overflow. Pick API parameter types that don't force lossy conversions on callers.

open as a page