views:

552

answers:

6

Hi,

This is a doubt regarding the representation of bits of signed integers. For example, when you want to represent -1, it is equivalent to 2's complement of (+1). So -1 is represented as 0xFFFFFFF. Now when I shift my number by 31 and print the result it is coming back as -1.

signed int a = -1;
printf(("The number is %d ",(a>>31));//this prints as -1

So can anyone please explain to me how the bits are represented for negative numbers?

Thanks.

+6  A: 

When the top bit is zero, the number is positive. When it's 1, the number is negative.

Negative numbers shifted right keep shifting a "1" in as the topmost bit to keep the number negative. That's why you're getting that answer.

For more about two's complement, see this Stackoverflow question.


@Stobor points out that some C implementations could shift 0 into the high bit instead of 1. [Verified in Wikipedia.] In Java it's dependably an arithmetic shift.

But the output given by the questioner shows that his compiler is doing an arithmetic shift.

Nosredna
But y shud it print as -1,it should print as 1 rite since the top most but is 1 which represents a negative number.
@Nosredna: you should also mention that the fill-with-sign-bit behaviour is implementation specific.
Stobor
Since it represents a negative number, why would it print a positive number?
Nosredna
@Stobor: Oh! Good point. I didn't know it was. Do you have a good reference on that? Does any compiler shift in zeros? Anyway, from his example I think his compiler is shifting in 1s.
Nosredna
http://msdn.microsoft.com/en-us/library/336xbhcz%28VS.80%29.aspx
Stobor
Stobor
Thanks. I found in Wikipedia and credited you in my answer.
Nosredna
Most the processors I've programmed had both ASR and LSR.
Nosredna
@Nosredna: Thanks. And good point - it's a matter of whether the complier implementations picks ASR or LSR for the `>>` operator. The "traditional" logic is ASR for signed numbers and LSR for unsigned, but that's up to the compiler implementor.
Stobor
LSR and ASR are the same for unsigned, so that choice is arbitrary. Only the signed case matters, right?
Nosredna
@Nosredna: the CPU registers don't know anything about signed vs unsigned - LSR does "what you'd expect" if you were interpreting the bits in the register as an unsigned number, ASR does "what you'd expect" if you were interpreting the bits as a signed number.
Stobor
Right, but you said the compiler, by tradition, uses ASR for signed and LSR for unsigned numbers. My point is that it doesn't matter whether it's LSR or ASR if the number is unsigned. I always loved programming in assembly because you could treat data as either signed or unsigned on the fly. In C, you get all these ugly casts in the way--having the choice be in the operator rather than the data was fun.
Nosredna
It does matter whether it's LSR or ASR if the number is unsigned. For example, suppose you have a register representing an unsigned int, and its current value is 0xFFFFFFFF. If you ASR it by 31 you get 0xFFFFFFFF, but if you LSR it by 31 you get 0x00000001. I'm pretty sure it matters which one happens. Of course if the value is a bit pattern which as a signed integer would be positive, then the two have the same effect, but positive != unsigned.
Steve Jessop
And the C standard requires that the >> operator on an unsigned value performs a logical shift, so it certainly matters to the standard even if not to you ;-)
Steve Jessop
I see it. I was missing your point entirely. Thanks.
Nosredna
A: 

Have a look at two's complement description. It should help.

stanigator
+1  A: 

This is an arithmetic shift operation which preserves the sign bit and shifts the mantissa part of a signed number.

cheers

Andriyev
+4  A: 

The C standard leaves it undefined whether the right shift of a negative (necessarily signed) integer shifts zeroes (logical shift right) or sign bits (arithmetic shift right) into the most significant bit. It is up to the implementation to choose.

Consequently, portable code ensures that it does not perform right shifts on negative numbers. Either it converts the value to the corresponding unsigned value before shifting (which is guaranteed to use a logical shift right, putting zeroes into the vacated bits), or it ensures that the value is positive, or it tolerates the variation in the output.

Jonathan Leffler
Or it could "or" in the top bit (or bits) after the shift.
Nosredna
Thanks a lot for all the answers.Guess i m not still cleared my doyubt yet.So is it compiler or hardware specific???
@Maddy: it depends on the hardware and the compiler. For example, if a particular CPU only has logical shift right (LSR) and not arithmetic shift right (ASR), then the compilers for that machine will use the LSR and not the non-existent ASR. It also depends on the compiler; even if a machine has both ASR and LSR, a compiler writer might decide to use LSR for both signed and unsigned quantities, for any of a number of reasons. It might be quicker; it might be consistent with other platforms where the same compiler also runs; it might be that the person writing the compiler doesn't like ASR.
Jonathan Leffler
+1  A: 

Basically there are two types of right shift. An unsigned right shift and a signed right shift. An unsigned right shift will shift the bits to the right, causing the least significant bit to be lost, and the most significant bit to be replaced with a 0. With a signed right shift, the bits are shifted to the right, causing the least significant bit be be lost, and the most significant bit to be preserved. A signed right shift divides the number by a power of two (corresponding to the number of places shifted), whereas an unsigned shift is a logical shifting operation.

The ">>" operator performs an unsigned right shift when the data type on which it operates is unsigned, and it performs a signed right shift when the data type on which it operates is signed. So, what you need to do is cast the object to an unsigned integer type before performing the bit manipulation to get the desired result.

Michael Aaron Safyan
Not necessarily a 'signed shift' for signed integers (though it is very commonly done that way) - it depends on the compiler and/or hardware.
Jonathan Leffler
Yeah. That's right. I've been programming for so long on well-behaved laptop / desktop / server machines running some variant of x86 and compiling with GCC, that it's often easy to forget the difference between what assumptions are reasonable for the current application and what the standard actually guarantees.
Michael Aaron Safyan
A: 

EDIT: When the below was written, the code in the question was written as:

unsigned int a = -1;
printf(("The number is %d ",(a>>31));//this prints as -1

If unsigned int is at least 32 bits wide, then your compiler isn't really allowed to produce -1 as the output of that (with the small caveat that you should be casting the unsigned value to int before you pass it to printf).

Because a is an unsigned int, assigning -1 to it must give it the value of UINT_MAX (as the smallest non-negative value congruent to -1 modulo UINT_MAX+1). As long as unsigned int has at least 32 bits on your platform, the result of shifting that unsigned quantity right by 31 will be UINT_MAX divided by 2^31, which has to fit within int. (If unsigned int is 31 bits or shorter, it can produce whatever it likes because the result of the shift is unspecified).

caf