Reputation: 113
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
Reputation: 58271
Output:
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 bit1
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.
Second printf:
Because i
is -1
, So in second expression -i - 1
== - (-1) - 1
== 1 - 1
== 0
so output is zero : 0
.
Upvotes: 4
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
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
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
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