CodeGym/Java Blog/Java Numbers/Widening and Narrowing of primitive types
Author
Aditi Nawghare
Software Engineer at Siemens

Widening and Narrowing of primitive types

Published in the Java Numbers group
members
Hi! As you have progressed through CodeGym, you've encountered primitive types many times. Here is a short list of what we know about them:
  1. They are not objects and represent a value stored in memory
  2. There are several kinds
    • Whole numbers: byte, short, int, long
    • Floating-point (fractional) numbers: float and double
    • Logical values: boolean
    • Symbolic values (for representing letters and numerals): char
  3. Each type has its own range of values:

Primitive type Size in memory Value range
byte 8 bits -128 to 127
short 16 bits -32768 to 32767
char 16 bits 0 to 65536
int 32 bits -2147483648 to 2147483647
long 64 bits -9223372036854775808 to 9223372036854775807
float 32 bits (2 to the power of -149) to ((2 - (2 to the power of -23)) * 2 to the power of 127)
double 64 bits (-2 to the power of 63) to ((2 to the power of 63) - 1)
boolean 8 (when used in arrays), 32 (if not used in arrays) true or false
But in addition to having different values, they also differ in how much space they occupy in memory. An int takes more than a byte. And a long is bigger than a short. The amount of memory occupied by primitives can be compared to Russian nesting dolls: Widening and narrowing of primitive types - 2 Each nesting doll has space available inside. The larger the nesting doll, the more space there is. A large nesting doll (long) will easy accommodate a smaller int. It easily fits and you don't need to do anything else. In Java, when working with primitives, this is called implicit conversion. Or put differently, it's called widening.

Widening in Java

Here's a simple example of a widening conversion:
public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       byte littleNumber = 16;

       bigNumber = littleNumber;
       System.out.println(bigNumber);
   }
}
Here we assign a byte value to an int variable. The assignment succeeds without any problems: the value stored in a byte takes up less memory than what an int can accommodate. The little nesting doll (byte value) easily fits inside the big nesting doll (int variable). It's a different matter if you try to do the opposite, i.e. to put a large value into a variable whose range can't accommodate such a big data type. With real nesting dolls, the number simply wouldn't fit. With Java, it can, but with nuances. Let's try putting an int into a short variable:
public static void main(String[] args) {

   int bigNumber = 10000000;

   short littleNumber = 1000;

   littleNumber = bigNumber;// Error!
   System.out.println(bigNumber);
}
Error! The compiler understands that you're trying to do something abnormal by shoving a large nesting doll (int) inside a small one (short). In this case, the compilation error is a warning from the compiler: "Hey, are you absolutely certain that you want to do this?" If you're certain, then you tell the compiler: "Everything is okay. I know what I'm doing!" This process is called explicit type conversion, or narrowing.

Narrowing in Java

To perform a narrowing conversion, you need to explicitly indicate the type that you want to convert your value to. In other words, you need to answer the compiler's question: "Well, which of these little nesting dolls do you want to put this big nesting doll into?" In our case, it looks like this:
public static void main(String[] args) {

   int bigNumber = 10000000;

   short littleNumber = 1000;

   littleNumber = (short) bigNumber;
   System.out.println(littleNumber);
}
We explicitly indicate that we want to put an int into a short variable and that we'll take the responsibility. Seeing that a narrower type has been explicitly indicated, the compiler performs the conversion. What's the result? Console output: -27008 That was a little unexpected. Why exactly did we get that? In fact, it's all very simple. Originally, the value was 10000000 It was stored in an int variable, which occupies 32 bits. This is its binary representation:
Widening and narrowing of primitive types - 3
We write this value into a short variable, which can only store 16 bits! Accordingly, only the first 16 bits of our number will be moved there. The rest will be discarded. As a result, the short variable receives the following value
Widening and narrowing of primitive types - 4
which in decimal form is equal to -27008 That is why the compiler asks you to "confirm" by indicating an explicit narrowing conversion to a specific type. First, this shows that you are taking responsibility for the result. And second, it tells the compiler how much space to allocate when converting happens. After all, in the last example, if we assigned an int value to a byte variable rather than a short, then we would only have 8 bits at our disposal, not 16, and the result would be different. Fractional types (float and double) have their own process for narrowing conversions. If you try casting a factional number to an integer type, the fractional part will be discarded.
public static void main(String[] args) {

   double d = 2.7;

   long x = (int) d;
   System.out.println(x);
}
Console output: 2

char

You already know that char is used to display individual characters.
public static void main(String[] args) {

   char c = '!';
   char z = 'z';
   char i = '8';

}
But this data type has several features that are important to understand. Let's look again at the table of value ranges:
Primitive type Size in memory Value range
byte 8 bits -128 to 127
short 16 bits -32768 to 32767
char 16 bits 0 to 65536
int 32 bits -2147483648 to 2147483647
long 64 bits -9223372036854775808 to 9223372036854775807
float 32 bits (2 to the power of -149) to ((2 - (2 to the power of -23)) * 2 to the power of 127)
double 64 bits (-2 to the power of 63) to ((2 to the power of 63) - 1)
boolean 8 (when used in arrays), 32 (if not used in arrays) true or false
The range 0 to 65536 is indicated for the char type. But what does that mean? After all, a char doesn't just represent numbers, but also letters, punctuation marks… The thing is that in Java char values are stored in Unicode format. We already encountered Unicode in one of the previous lessons. You probably remember that Unicode is a character encoding standard that includes the symbols of almost all the written languages of the world. In other words, it's a list of special codes that represent nearly every character in any language. The entire Unicode table is very large, and, of course, there's no need to learn it by heart. Here's a small part of it: Widening and narrowing of primitive types - 5 The main thing is to understand how chars are stored, and to remember that if you know the code for a particular character, you can always produce that character in your program. Let's try with some random number:
public static void main(String[] args) {

   int x = 32816;

   char c = (char) x ;
   System.out.println(c);
}
Console output: 耰 This is the format used to store chars in Java. Each symbol corresponds to a number: a 16-bit (two-byte) numeric code. In Unicode, 32816 corresponds to the Chinese character 耰. Make note of the following point. In this example, we used an int variable. It occupies 32 bits in memory, while a char occupies 16. Here we chose an int, because our number (32816) won't fit in a short. Although the size of a char (just like a short) is 16 bits, there are no negative numbers in the char range, so the "positive" part of the char range is twice as large (65536 instead of 32767 for the short type). We can use an int as long as our code stays below 65536. But if you create an int value greater than 65536, then it will occupy more than 16 bits. And this will result in a narrowing conversion
char c = (char) x;
the extra bits will be discarded (as discussed above) and the result will be quite unexpected.

Special features of adding chars and integers

Let's look at an unusual example:
public class Main {

   public static void main(String[] args) {

      char c = '1';

      int i = 1;

       System.out.println(i + c);
   }
}
Console output: 50 O_О How does that make sense? 1+1. Where did the 50 come from?! You already know that char values are stored in memory as numbers in the range from 0 to 65536, and that these numbers are a Unicode representation of a character. Widening and narrowing of primitive types - 6 When we add a char and some whole-number type, the char is converted to the corresponding Unicode number. In our code, when we added 1 and '1', the symbol '1' was converted to its own code, which is 49 (you can verify this in the table above). Therefore, the result is 50. Let's once again take our old friend 耰 as an example, and try adding it to some number.
public static void main(String[] args) {

   char c = '耰';
   int x = 200;

   System.out.println(c + x);
}
Console output: 33016 We already discovered that 耰 corresponds to 32816. And when we add this number and 200, we get our result: 33016. :) As you can see, the algorithm here is quite simple, but you shouldn't forget it.
Comments (19)
  • Popular
  • New
  • Old
You must be signed in to leave a comment
Robert
Level 17 , Debica, Poland
22 August 2021, 07:44
public static void main(String[] args) {

   int bigNumber = 10000000;

   short littleNumber = 1000;

   littleNumber = bigNumber;// Error!
   System.out.println(bigNumber);
}
I think, there is a mistake, we want to know littleNumber, line 9, we should print littleNumber ???
null
Level 12
3 March 2022, 06:37
yes,youre right
Kou Shikyo
Level 20 , Tokyo
3 October 2020, 18:16
耰 , Even I am a chinese , I have never used this word..
SirSkii
Level 12 , Chicago, United States
6 October 2020, 00:53
@Kou Shikyo can you translate the word to english? lol
SirSkii
Level 12 , Chicago, United States
6 October 2020, 00:56
Why'd I ask? (google) just did. nice to know
Kou Shikyo
Level 20 , Tokyo
6 October 2020, 00:58
It's a ancient chinese famer Tool which is used for turn the soil.
Agent Smith
Level 38
22 August 2020, 22:42
A nice little table:
Onur Bal
Level 27 , Istanbul, Turkey
13 September 2020, 11:19
The table in this lesson points out the amount of bits for a boolean is
8 (when used in arrays), 32 (if not used in arrays)
Why is this the case? I always thought it would be 1 bit as the table you shared has it, since it can either be true (1) or false (0), and nothing but.
Agent Smith
Level 38
13 September 2020, 16:10
Hmm, here is a quote from Oracle Java tutorials This data type represents one bit of information, but its "size" isn't something that's precisely defined. And according to this discussion on stackoverflow it seems that the exact size depends on the virtual machine.
Peter Schrijver
Level 23 , Hilversum, Netherlands
11 June 2020, 11:48
Nice explanation, good reference material for later
Austeja
Level 10 , Kaunas, Lithuania
22 May 2020, 06:05
Chars are really interesting. I bet one can use them very creatively.
Ashish RajAnand
Level 13 , Bhilai , India
21 April 2020, 02:21
nice
Vahan
Level 41 , Tbilisi, Georgia
9 August 2019, 08:56
What are the code at wright part of square in last table, as 43 * 2A? What this 2A means?
Artur Jarosz
Level 30 , Lublin, Poland
11 November 2019, 20:57
2A is a hex code for 43.
Pablo Souza
Level 22 , Brisbane, Australia
3 April 2020, 06:26
"Hexadecimal (or "hex" for short) is a numbering system which works similarly to our regular decimal system, but where a single digit can take a value of 0-15 rather than 0-9. The extra digits are represented by the letters A-F, as shown in the table to the right.". Found at https://www.javamex.com/tutorials/conversion/decimal_hexadecimal.shtml
Renat Mukhametshin
Level 16 , Pervouralsk, Russain Federation
8 August 2019, 06:23
ok, good!
8 July 2019, 22:47
Same question here than Eweton. Isn't boolean just 1 or 0? there's only 1 bit needed for that haha
Ewerton Backend Developer
29 June 2019, 11:01
Why would they need 32 bits for a boolean?
Łukasz
Level 16 , GDANSK, POLAND
22 August 2020, 15:49
yes, boolean values are manipulated as 32-bit entities, but arrays of booleans use 1 byte per element.