Integral data types in Java and C#

Here is something not super interesting, but that usually comes in handy: what's the range of the integral types in Java and C#?

Let's start by saying that integral data types are those that represent only whole numbers. Each number has a well defined successor, so floating point numbers are not integral data types (what comes after 0.1? 0.11 or 0.111?) but integers are.

That being said, Java supports the following integral data types: byte, short, int, and long. On the other hand, C# supports sbyte, byte, short, ushort, int, uint, long and ulong. On purpose, I'm leaving outside of this conversation the char data type since, despite it's considered an integral type, I don't want to confuse anyone by mixing characters with numbers. So a total of 4 types in Java and 8 types in C#.

The interesting part is the range of numbers that each one of these types supports. To calculate this range, let's first take a look to the size of each one of these types:

sbyte - 8 bits (1 Byte)
byte - 8 bits (1 Byte)
short - 16 bits (2 Bytes)
ushort - 16 bits (2 Bytes)
int - 32 bits (4 Bytes)
uint - 32 bits (4 Bytes)
long - 64 bits (8 Bytes)
ulong - 64 bits (8 Bytes)

This is true for both Java and C# (remember that only a subset of these types is supported in Java). Having these sizes, there's a simple formula to compute the range of each type. In the case of C# we also need to pay attention to the prefix (if any) of the type. This prefix can be "s" (sbyte), "u" (uint), or none (long). This hints whether the range is going to include negative numbers or not: "s" means "signed" so it will include negative numbers, "u" means "unsigned" so it will include only positive numbers, and when nothing is specified, assume negative numbers are included. So let's see the formula:

  • If the number includes negative values, the lower bound is going to be - 2^(bits - 1).
  • If the number includes negative values, the upper bound is going to be 2^(bits - 1) - 1.
  • If no negative numbers are included, the lower bound is going to be 0.
  • If no negative numbers are included, the upper bound is going to be 2^bits.

The reason why for negative numbers we need to use bits - 1 as the exponential part of the formula is because the sign (+ or -) is going to be stored on the same value and it will take 1 bit (1 meaning negative and 0 meaning positive). Let's run the numbers and define the ranges:

sbyte [-2^7 .. 2^7 - 1] = [-128 .. 127] 
byte (Java) [-2^7 .. 2^7 - 1] = [-128 .. 127]
byte (C#) [0 .. 2^8] = [0 .. 255]
short [-2^15 .. 2^15 - 1] = [-32,768 .. 32,767]
ushort [0 .. 2^16] = [0 .. 65,535]
int [-2^31 .. 2^31 - 1] = [-2,147,483,648 .. 2,147,483,647]
uint [0 .. 2^32] = [0 .. 4,294,967,295]
long [-2^63 .. 2^63 - 1] = [-9,223,372,036,854,775,808 .. 9,223,372,036,854,775,807]
ulong [0 .. 2^64] = [0 .. 18,446,744,073,709,551,615]

So here are some things to notice: First, if you use both C# and Java, be careful when you use the byte data type because it doesn't have the same range in both languages. An equivalent for the Java's byte in C# is sbyte. Second, note that in case of signed types, the upper bound is always 1 less than the lower bound (32,767 vs 32,768 for an int). This is because the value 0 is always considered a positive number. Third, try to say out aloud the number represented by a long value. It will probably take you a while.

Another point that is usually misunderstood is the fact that the size of these data types is going to be always the same regardless of the architecture our program is running on. You might be running your program in a 64 bit machine, but an int is going to be always 32 bit long and it will have the same range. This is true for both C# and Java.

Have something to say about this post? Get in touch!

Want to read more? Visit the archive.