14

The number 1, right shifted by anything greater than 0, should be 0, correct? Yet I can type in this very simple program which prints 1.

#include <stdio.h>

int main()
{
        int b = 0x80000000;
        int a = 1 >> b;
        printf("%d\n", a);
}

Tested with gcc on linux.

Jeff Linahan
  • 3,775
  • 5
  • 37
  • 56
  • 27
    Technically, it's undefined behavior, since you're shifting of more than the width of the type (which gcc should report in a warning). – Matteo Italia Sep 29 '11 at 20:54
  • 2
    Unless you cast it, wouldn't that be asking it to shift by -1? – Marvo Sep 29 '11 at 20:54
  • 3
    Also, it seems that `gcc` fully respects the spirit of UB, by giving different "wrong" results depending on the conditions (http://gcc.gnu.org/ml/gcc/2004-11/msg01131.html) – Matteo Italia Sep 29 '11 at 20:57
  • 1
    @MatteoItalia: What meaning does the word "Technically" add to your comment? It weakens the (perfectly valid) point that the behavior is *undefined*. And I wouldn't expect a warning from gcc unless you use `-O` to enable optimization; without dataflow analysis, the compiler doesn't know that `b` is greater than *or equal to* the width of `int`. – Keith Thompson Sep 29 '11 at 21:11
  • @KeithThompson: more than "technically" I should have written "as per the standard"; I wrote that comment just as a remark, because I thought that he wanted to know, more than what the standard says, why is he getting that result (=>from what comes that particular manifestation of UB). As for the warning, I mentioned that because I got it on Ideone. :) – Matteo Italia Sep 29 '11 at 21:22
  • 1
    @MatteoItalia: Understood. Unfortunately, a lot of people use the word "technically" to mean "Well, the standard says this, but in practice you don't need to worry about it". And I don't want the OP to assume the code is ok if he doesn't see a warning. – Keith Thompson Sep 29 '11 at 21:26
  • @Marvo: Not necessarily. If `0x80000000` cannot be represented by an `int`, the result of the conversion is implementation-defined. – caf Sep 30 '11 at 05:56
  • Possible duplicate of [What does the C standard say about bitshifting more bits than the width of type?](https://stackoverflow.com/questions/11270492/what-does-the-c-standard-say-about-bitshifting-more-bits-than-the-width-of-type) – phuclv Sep 15 '18 at 01:22
  • @phuclv actually I asked this question about a year before that one – Jeff Linahan Sep 15 '18 at 02:49

4 Answers4

30

6.5.7 Bitwise shift operators:

If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

The compiler is at license to do anything, obviously, but the most common behaviors are to optimize the expression (and anything that depends on it) away entirely, or simply let the underlying hardware do whatever it does for out-of-range shifts. Many hardware platforms (including x86 and ARM) mask some number of low-order bits to use as a shift-amount. The actual hardware instruction will give the result you are observing on either of those platforms, because the shift amount is masked to zero. So in your case the compiler might have optimized away the shift, or it might be simply letting the hardware do whatever it does. Inspect the assembly if you want to know which.

Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
  • @MooingDuck: You can't infer that from the observed behavior. There are several other possible explanations. – Stephen Canon Sep 29 '11 at 21:03
  • I've heard that some compilers implement this behavior by just using the five rightmost bits of the RHS, which explains the OP's behavior. – Mooing Duck Sep 29 '11 at 21:03
  • That's it then, i was trying to think of a clever way to implement ! using only bitwise operations, this was the only test case that failed. – Jeff Linahan Sep 29 '11 at 21:05
  • 1
    @da code monkey: Why? `not` will almost certainly be as fast as, if not faster than, a bit-shift. – BlueRaja - Danny Pflughoeft Sep 29 '11 at 22:14
  • I usually do use the not operator, however this was for a programming puzzle. – Jeff Linahan Sep 29 '11 at 22:47
  • If you're just trying to go between 0 and another value that you don't care about you can use `~foo`, which is bitwise inversion. Or if you want to go between 0 and 1 you can use `foo^1`. Both of those obviously just toggle between 0 and a specific other value, though. – fluffy Sep 30 '11 at 00:06
  • yeah, the puzzle was to return 1 when 0 was the input – Jeff Linahan Sep 30 '11 at 01:08
2

according to the standard, shifting for more than the bits actually existing can result in undefined behavior. So we cannot blame the compiler for that.

The motivation probably resides in the "border meaning" of 0x80000000 that sits on the boundary of the maximum positive and negative together (and that is "negative" having the highmost bit set) and on certain check that should be done and that the compiled program doesn't to to avoid to waste time verifying "impossible" things (do you really want the processor to shift bits 3 billion times?).

Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
1

It's very probably not attempting to shift by some large number of bits.

INT_MAX on your system is probably 2**31-1, or 0x7fffffff (I'm using ** to denote exponentiation). If that's the case, then In the declaration:

int b = 0x80000000;

(which was missing a semicolon in the question; please copy-and-paste your exact code) the constant 0x80000000 is of type unsigned int, not int. The value is implicitly converted to int. Since the result is outside the bounds of int, the result is implementation-defined (or, in C99, may raise an implementation-defined signal, but I don't know of any implementation that does that).

The most common way this is done is to reinterpret the bits of the unsigned value as a 2's-complement signed value. The result in this case is -2**31, or -2147483648.

So the behavior isn't undefined because you're shifting by value that equals or exceeds the width of type int, it's undefined because you're shifting by a (very large) negative value.

Not that it matters, of course; undefined is undefined.

NOTE: The above assumes that int is 32 bits on your system. If int is wider than 32 bits, then most of it doesn't apply (but the behavior is still undefined).

If you really wanted to attempt to shift by 0x80000000 bits, you could do it like this:

unsigned long b = 0x80000000;
unsigned long a = 1 >> b;    // *still* undefined

unsigned long is guaranteed to be big enough to hold the value 0x80000000, so you avoid part of the problem.

Of course, the behavior of the shift is just as undefined as it was in your original code, since 0x80000000 is greater than or equal to the width of unsigned long. (Unless your compiler has a really big unsigned long type, but no real-world compiler does that.)

The only way to avoid undefined behavior is not to do what you're trying to do.

It's possible, but vanishingly unlikely, that your original code's behavior is not undefined. That can only happen if the implementation-defined conversion of 0x80000000 from unsigned int to int yields a value in the range 0 .. 31. IF int is smaller than 32 bits, the conversion is likely to yield 0.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • The last paragraph is not so vanishingly unlikely - it would be quite likely on a system with 16 bit `int`, which is far from unheard-of. – caf Sep 30 '11 at 06:00
0

well read that maybe can help you

expression1 >> expression2

The >> operator masks expression2 to avoid shifting expression1 by too much.

That because if the shift amount exceeded the number of bits in the data type of expression1, all the original bits would be shifted away to give a trivial result.

Now for ensure that each shift leaves at least one of the original bits, the shift operators use the following formula to calculate the actual shift amount:

mask expression2 (using the bitwise AND operator) with one less than the number of bits in expression1.

Example

var x : byte = 15;
// A byte stores 8 bits.
// The bits stored in x are 00001111
var y : byte = x >> 10;
// Actual shift is 10 & (8-1) = 2
// The bits stored in y are 00000011
// The value of y is 3
print(y); // Prints 3

That "8-1" is because x is 8 bytes so the operacion will be with 7 bits. that void remove last bit of original chain bits

Carlos Cocom
  • 937
  • 1
  • 8
  • 23