jsutterfield
jsutterfield

Reputation: 113

Calculating Ranges of Data Types in C

I'm working through K&R Second Edition, and can't figure out why I'm getting a certain result. The problem I'm solving is calculating upper and lower limits for data types. Specifically:

"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."

I've learned about bitwise operators and two's compliment, and have a solution I think should work for signed data types, but instead it's working for unsigned data types which doesn't make any sense to me. Here's the code:

#include <stdio.h>

main()
{
    signed int i;

    i = ~0;
    i >>= 1;
    printf("Upper limit: %d\n", i);
    printf("Lower limit: %d\n", -i -1);
}

This will result in -1 being printed for the upper limit, and 0 printed for the lower limit. However, if I change i to an unsigned int, I get the result I was expecting (2147483647 and -2147483648). I can't wrap my head around this, because my understanding is that an unsigned int can never be less than 0, and a signed int should work using these bitwise operators, ie, if it's a 32 bit system,

~0 == 11111111111111111111111111111111

, and

~0 >> 1 == 011111111111111111111111111111111, 
           or 2147483647.

Any idea where I'm going wrong?

Upvotes: 5

Views: 5610

Answers (5)

Grijesh Chauhan
Grijesh Chauhan

Reputation: 58271

Output:

  1. Note:
    “In the expression i >>= 1, a negative value is shifted right. The C standard says this is an implementation-defined operation, and many implementations define it to be arithmetic shift. In an arithmetic shift, the most significant bit is unchanged (keeps MSB (signed bit) = 1)".

    (you can read: Right shifting negative numbers in C that >> is compiler dependent whether its singed or unsinfed shift, but probably in your case its doing an Arithmetic Shift.)

    For this reason after code:

     i = ~0;  
     i >>= 1;
    

    i remains ~0. that is in binary == 11111111111111111111111111111111.

    And because ~0 == 11111111111111111111111111111111 is == 2'c complement of 1 that is -1.

    So when you prints with format string %d it print -1. You should use %u to print max unsigned value that is == ~0.

    Important to note here:

    §6.2.6.2 Language 45, ©ISO/IEC ISO/IEC 9899:201x

    (ones’ complement). Which of these applies is implementation-defined, as is whether the value with sign bit 1 and all value bits zero (for the first two), or with sign bit and all value bits 1 (for ones’ complement), is a trap representation or a normal value. In the case of sign and magnitude and ones’ complement, if this representation is a normal value it is called a negative zero.

    Your understanding that :

    ~0 >> 1 == 011111111111111111111111111111111 is wrong! (it may be but not happening in your system, according to output)

    ~0 >> 1 == 111111111111111111111111111111111, note MSB(signed bit) is 1.

    For unsigned shift, try following:

    ~0U >> 1 == 011111111111111111111111111111111

    Notice Suffix U for unsigned.

  2. Second printf:
    Because i is -1, So in second expression -i - 1 == - (-1) - 1 == 1 - 1 == 0 so output is zero : 0.

Upvotes: 4

RiaD
RiaD

Reputation: 47620

You may be interested in constant in limits.h and float.h header files

From limits.h:

+------------+------------------------------------------------------------------+--------------------------------+
| CHAR_BIT   | Number of bits in a char object (byte)                           | 8 or greater                   |
| SCHAR_MIN  | Minimum value for an object of type signed char                  | -127 (-2^7+1) or less          |
| SCHAR_MAX  | Maximum value for an object of type signed char                  | 127 (2^7-1) or greater         |
| UCHAR_MAX  | Maximum value for an object of type unsigned char                | 255 (2^8-1) or greater         |
| CHAR_MIN   | Minimum value for an object of type char                         | either SCHAR_MIN or 0          |
| CHAR_MAX   | Maximum value for an object of type char                         | either SCHAR_MAX or UCHAR_MAX  |
| MB_LEN_MAX | Maximum number of bytes in a multibyte character, for any locale | 1 or greater                   |
| SHRT_MIN   | Minimum value for an object of type short int                    | -32767 (-2^15+1) or less       |
| SHRT_MAX   | Maximum value for an object of type short int                    | 32767 (2^15-1) or greater      |
| USHRT_MAX  | Maximum value for an object of type unsigned short int           | 65535 (2^16-1) or greater      |
| INT_MIN    | Minimum value for an object of type int                          | -32767 (-2^15+1) or less       |
| INT_MAX    | Maximum value for an object of type int                          | 32767 (2^15-1) or greater      |
| UINT_MAX   | Maximum value for an object of type unsigned int                 | 65535 (2^16-1) or greater      |
| LONG_MIN   | Minimum value for an object of type long int                     | -2147483647 (-2^31+1) or less  |
| LONG_MAX   | Maximum value for an object of type long int                     | 2147483647 (2^31-1) or greater |
| ULONG_MAX  | Maximum value for an object of type unsigned long int            | 4294967295 (2^32-1) or greater |
+------------+------------------------------------------------------------------+--------------------------------+

Upvotes: 3

Michael Graczyk
Michael Graczyk

Reputation: 4965

When you perform the bit shift on i, the compiler sees that i is a signed quantity, and performs an arithmetic right shift. It seems like you want that line of code to perform a logical right shift.

Change the line

i >>= 1;

to

i = ((unsigned int)i) >> 1;

Then it works!

Output:
Upper limit: 2147483647
Lower limit: -2147483648

Upvotes: 1

DasKr&#252;melmonster
DasKr&#252;melmonster

Reputation: 6060

Your compiler implements >> as arithmetic shift. Therefore, the MSB keeps it value of 1 and the shift does nothing.

That is, ~0 >> 1 is still ~0 because the shift sign-extends.

See here: https://stackoverflow.com/a/7632/1974021

Upvotes: 3

triclosan
triclosan

Reputation: 5714

by using %d you treat your value as signed to proceed by printf.

you may use %u instead.

added

As Magn3s1um pointed out you don't need to specify signed and unsigned for your particular task printf will make all job for you.

Upvotes: 6

Related Questions