5

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?

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
jsutterfield
  • 113
  • 2
  • 6
  • @Magn3s1um: but it does not hurt at all. – wallyk Jul 22 '13 at 19:46
  • 1
    To understand what is Unsigned and Singed right shift read [Signed right shift & Unsigned](http://stackoverflow.com/questions/15457893/java-right-shift-on-negative-number/15457908#15457908) operator doesn't exit in C but concept does. – Grijesh Chauhan Jul 22 '13 at 20:15
  • to calculate data range I written a code in my answer [what is the value of `~0` in C?](http://stackoverflow.com/a/23180179/1673391) – Grijesh Chauhan Apr 21 '14 at 07:14

5 Answers5

6

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.

triclosan
  • 5,578
  • 6
  • 26
  • 50
  • 2
    @jsutterfield: If you had enabled all warnings (with `gcc`) a warning for formating an unsigned using a signed conversion (%d) would have appeared alerting you to the issue. – wallyk Jul 22 '13 at 19:48
4

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.

Community
  • 1
  • 1
Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
  • 1
    The C standard does not define `i >>= 1` to be a signed shift. If `i` is negative, the behavior is undefined by the standard. It might be a right shift in the OP’s implementation, but it might not be, even though it produced -1 in this instance. – Eric Postpischil Jul 22 '13 at 20:22
  • @EricPostpischil True but I am writing according to OP output, that why I didn't write that because `i` is singed so `>>` is singed shift – Grijesh Chauhan Jul 22 '13 at 20:24
  • (a) It is not possible to determine from a single instance whether the shift is an arithmetic shift in the OP’s implementation. Doing so requires knowing the specification of the implementation. (b) To avoid giving learners the wrong impression about the language specification, blanket statements that a right-shift of an `int` is signed should be avoided. The assumptions should be explicitly stated. – Eric Postpischil Jul 22 '13 at 20:26
  • @EricPostpischil if I doesn't say that `>>` in your case is singed then how can I explain the output, How should I write. (suggest me/ or edit for me in my answer so It not misguide) – Grijesh Chauhan Jul 22 '13 at 20:30
  • @EricPostpischil should I quote as [here](http://stackoverflow.com/questions/15729765/why-does-the-output-of-applied-on-a-negative-number-is-filled-with-ones-on-th/15730750#15730750) But I feel it will confuse new guy – Grijesh Chauhan Jul 22 '13 at 20:32
  • 1
    The first sentence could be changed “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.” – Eric Postpischil Jul 22 '13 at 20:37
  • @EricPostpischil I recently came to know about this that integer overflow is Undefined in C. But are you sure that `~0` will be undefined. because what I know its Undefined in mathematics operation. Thanks for you correction I am just updating. – Grijesh Chauhan Jul 22 '13 at 20:40
  • @EricPostpischil Many many thanks! to helping me to improve my answer. Just after your correcting my answer *presently* chosen.--Thanks! – Grijesh Chauhan Jul 22 '13 at 20:55
  • 1
    `~0` produces a value with all bits one. According to C 2011 (N1570) 6.2.6.2 paragraph 2, whether this is a trap representation or a normal value is implementation-defined. Since the OP is asking about code to explore an unknown C implementation, one should not rely on implementation-defined behavior. If `~0` is a trap representation, then the behavior is undefined. – Eric Postpischil Jul 22 '13 at 20:55
  • @EricPostpischil you are correct I just read here [§6.2.6.2 Language 45](https://is.muni.cz/www/408176/38744863/International_Standard_for_C.txt) – Grijesh Chauhan Jul 22 '13 at 21:08
  • @EricPostpischil As I had discussion with you: read this [`FPE_INTOVF_TRAP`: Integer overflow (impossible in a C program unless you enable overflow trapping in a hardware-specific fashion)](http://www.cs.utah.edu/dept/old/texinfo/glibc-manual-0.02/library_21.html#SEC336), So you do you think an integer overflow cause an SIGFPE signal and process terminate in some C implementation **?** as its implementation-defined behavior – Grijesh Chauhan Aug 15 '13 at 06:43
3

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

Community
  • 1
  • 1
DasKrümelmonster
  • 5,816
  • 1
  • 24
  • 45
3

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 |
+------------+------------------------------------------------------------------+--------------------------------+
Kninnug
  • 7,992
  • 1
  • 30
  • 42
RiaD
  • 46,822
  • 11
  • 79
  • 123
  • 2
    The homework assignment is "Write a program to determine the ranges ..." Your solution comes down to "Write a program to show the contents of limits.h". – Jongware Jul 22 '13 at 21:09
1

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
Michael Graczyk
  • 4,905
  • 2
  • 22
  • 34