Exercise 2.1 - Count the Ranges

Question

Write a program to determine the ranges of char, short, int, and long variables, both signed and unsigned, by printing appropriate values from standard headers and by direct computation. Harder if you compute them: determine the ranges of the various floating-point types.

/**
 * Exercise 2.1
 *
 * Write a program to print maximum, minimum limits of char, int, long using
 * calculation.
 *
 */

/**
 * Priminary Information:
 *
 * ~0 will give bits in 1s.
 * >> 1 right shift by 1 bit to remove the sign bit.
 * -1 is added to the maximum limit to get the minimum limit.
 *
 * The maximum and minimum limits of various integer types can be calculated
 * using the following logic:
 *
 * 1. The maximum limit of a signed integer type can be calculated by
 *   (unsigned <type>) ~0 >> 1
 *
 * 2. The minimum limit of a signed integer type can be calculated by
 *  ((unsigned <type>) ~0 >> 1) - 1
 *   *
 * 3. We cast it back to the required type with sign to print the value.
 *
 */

#include <float.h>
#include <limits.h>
#include <stdio.h>

int main() {
    /* ranges of various integer types through calculation */
    printf("Ranges of various integer types through calculation:\n");

    printf("Minimum Signed Char %d\n", -(int)((unsigned char)~0 >> 1) - 1);
    printf("Maximum Signed Char %d\n", (int)((unsigned char)~0 >> 1));

    printf("Minimum Signed Short %d\n", -(int)((unsigned short)~0 >> 1) - 1);
    printf("Maximum Signed Short %d\n", (int)((unsigned short)~0 >> 1));

    printf("Minimum Signed Int %d\n", -(int)((unsigned int)~0 >> 1) - 1);
    printf("Maximum Signed Int %d\n", (int)((unsigned int)~0 >> 1));

    printf("Minimum Signed Long %ld\n", -(long)((unsigned long)~0 >> 1) - 1);
    printf("Maximum signed Long %ld\n", (long)((unsigned long)~0 >> 1));

    /* Unsigned Maximum Values */

    printf("Maximum Unsigned Char %d\n", (unsigned char)~0);
    printf("Maximum Unsigned Short %d\n", (unsigned short)~0);
    printf("Maximum Unsigned Int %u\n", (unsigned int)~0);
    printf("Maximum Unsigned Long %lu\n\n", (unsigned long)~0UL);

    /* Calculating max values of float types can be tricky, we can use the standard headers */

    /* ranges of various floating-point types from standard headers */
    printf("Ranges of various integer and floating-point types from standard headers:\n");
    printf("Minimum Signed Char %d\n", SCHAR_MIN);
    printf("Maximum Signed Char %d\n", SCHAR_MAX);

    printf("Minimum Signed Short %d\n", SHRT_MIN);
    printf("Maximum Signed Short %d\n", SHRT_MAX);

    printf("Minimum Signed Int %d\n", INT_MIN);
    printf("Maximum Signed Int %d\n", INT_MAX);

    printf("Minimum Signed Long %ld\n", LONG_MIN);
    printf("Maximum signed Long %ld\n", LONG_MAX);

    printf("Minimum Signed Long Long %lld\n", LLONG_MIN);
    printf("Maximum Signed Long Long %lld\n", LLONG_MAX);

    printf("Minimum Float %E\n", FLT_MIN);
    printf("Maximum Float %E\n", FLT_MAX);

    printf("Minimum Double %E\n", DBL_MIN);
    printf("Maximum Double %E\n", DBL_MAX);

    printf("Minimum Long Double %LE\n", LDBL_MIN);
    printf("Maximum Long Double %LE\n", LDBL_MAX);

    /* Unsigned Maximum Values */

    printf("Maximum Unsigned Char %d\n", UCHAR_MAX);
    printf("Maximum Unsigned Short %d\n", USHRT_MAX);
    printf("Maximum Unsigned Int %u\n", UINT_MAX);
    printf("Maximum Unsigned Long %lu\n", ULONG_MAX);
    printf("Maximum Unsigned Long Long %llu\n", ULLONG_MAX);

    return 0;
}

Explanation

The execution of the above program will give:

Ranges of various floating-point types through calculation:
Minimum Signed Char -128
Maximum Signed Char 127
Minimum Signed Short -32768
Maximum Signed Short 32767
Minimum Signed Int -2147483648
Maximum Signed Int 2147483647
Minimum Signed Long -9223372036854775808
Maximum signed Long 9223372036854775807
Maximum Unsigned Char 255
Maximum Unsigned Short 65535
Maximum Unsigned Int 4294967295
Maximum Unsigned Long 18446744073709551615

Ranges of various integer and floating-point types from standard headers:
Minimum Signed Char -128
Maximum Signed Char 127
Minimum Signed Short -32768
Maximum Signed Short 32767
Minimum Signed Int -2147483648
Maximum Signed Int 2147483647
Minimum Signed Long -9223372036854775808
Maximum signed Long 9223372036854775807
Minimum Signed Long Long -9223372036854775808
Maximum signed Long Long 9223372036854775807
Minimum Float 1.175494E-38
Maximum Float 3.402823E+38
Minimum Double 2.225074E-308
Maximum Double 1.797693E+308
Minimum Long Double 3.362103E-4932
Maximum Long Double 1.189731E+4932
Maximum Unsigned Char 255
Maximum Unsigned Short 65535
Maximum Unsigned Int 4294967295
Maximum Unsigned Long 18446744073709551615
Maximum Unsigned Long Long 18446744073709551615

In order explain how we calculate the maximum values, we will have to do some bit arthimetic. Let’s start by calculating the maximum unsigned char.

Sign bit is usually the left most bit of the type. To make it unsigned, we remove the sign bit and store 0 in it’s place.

To represent (unsigned char) 0 = 0000 0000 To represent it’s complement, (unsigned char) ~0 = 1111 1111

Right Shift 1 will shift that entire sequence to the right and insert 0 from the left side, so it will give 0111 1111

Converting 0111 1111 to integer will be:

0 * 2^7 + 1 * 2^ 6 + 1 * 2^5 + 1 * 2^4 + 1 * 2^3 + 1 *2^2 + 1 * 2^1 + 1 * 2^0
= 0 * 0 + 1 * 64 + 1 * 32 + 1 * 16 + 1 * 8 + 1 * 4 + 1 * 2 + 1 * 1
= 0 + 64 + 32 + 16 + 8 + 4 + 2 + 1
= 127

That is the maximum signed char value. To get the minimum signed char value, we look for the number in the other end of the number line.

That is got by, multiplying it by -1 and going one number further to the left in the number line:

= - 127 -1
= -128

So our range is displayed like this:

-128..-2..-1..0 1 2 3 4... 127

Maximum signed short

To get the maximum signed short number, we start with unsigned short type again and repeat the same operations like we did above.

(short)((unsigned short)~0 >> 1)

In our compiler, a short is 2 bytes

(unsigned short) 0 means 0000 0000 0000 0000 (unsigned short) ~0 means 1111 1111 1111 1111

Logical right shift, is we are eliminating the sign bit, which is the left most bit in the sequence.

(unsigned short)~0 >> 1)

Take this portion 1111 1111 1111 111 1 and place it as 1111 1111 1111 111 And then add 0 to the left 0 1111 1111 1111 111 Rearranging we get 0111 1111 1111 1111

What is value of 0111 1111 1111 1111 ?

Similar to above:

=   0 * 2^15 + 1 * 2^14 + 1 * 2^13 + 1 * 2^12
    + 1 * 2^11 + 1* 2^10 + 1 * 2^9 + 1 * 2^8
    + 1*2^7 + 1 * 2^6 + 1*2^5 + 1*2^4
    + 1* 2^3 + 1*2^2 + 1* 2^1 +1*2^0

    = 16384 + 8192 + 4096 + 2048 + 1024 + 512 + 256 + 128 + 64 + 32 + 16 + 8 + 4 + 2
    = + 1 32767

So, our maximum unsigned short is 32767

Mininum unsigned short will be the negative of that number going one step further, i.e:

= -32767 - 1 = -32768

Similarily, we can calculate for int and long types.

For unsigned types, we do not do the shifting (to remove the sign) but simply print the value of all 1s of the type

(unsigned char)~0;

can be represented as (unsigned char) ~0 = 1111 1111

= 1 * 2^7 + 1 * 2^ 6 + 1 * 2^5 + 1 * 2^4 + 1 * 2^3 + 1 *2^2 + 1 * 2^1 + 1 * 2^0
= 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1
= 255