Weird behavior of right shift in C (sometimes arithmetic, sometimes logical)

3

GCC version 5.4.0 Ubuntu 16.04

I have noticed some weird behavior with the right shift in C when I store a value in variable or not.

This code snippet is printing 0xf0000000, the expected behavior

int main() {
    int x = 0x80000000
    printf("%x", x >> 3);
}

These following two code snippets are printing 0x10000000, which is very weird in my opinion, it is performing logical shifts on a negative number

1.

int main() {
    int x = 0x80000000 >> 3
    printf("%x", x);
}

2.

int main() {
    printf("%x", (0x80000000 >> 3));
}

Any insight would be really appreciated. I do not know if it a specific issue with my personal computer, in which case it can't be replicated, or if it is just a behavior in C.

c
asked on Stack Overflow Sep 14, 2018 by Suhas

3 Answers

3

Quoting from https://en.cppreference.com/w/c/language/integer_constant, for an hexadecimal integer constant without any suffix

The type of the integer constant is the first type in which the value can fit, from the list of types which depends on which numeric base and which integer-suffix was used.

int
unsigned int
long int
unsigned long int
long long int(since C99)
unsigned long long int(since C99)

Also, later

There are no negative integer constants. Expressions such as -1 apply the unary minus operator to the value represented by the constant, which may involve implicit type conversions.

So, if an int has 32 bit in your machine, 0x80000000 has the type unsigned int as it can't fit an int and can't be negative.

The statement

int x = 0x80000000;

Converts the unsigned int to an int in an implementation defined way, but the statement

int x = 0x80000000 >> 3;

Performs a right shift to the unsigned int before converting it to an int, so the results you see are different.

EDIT

Also, as M.M noted, the format specifier %x requires an unsigned integer argument and passing an int instead causes undefined behavior.

answered on Stack Overflow Sep 14, 2018 by Bob__ • edited Sep 14, 2018 by Bob__
0

Right shift of the negative integer has implementation defined behavior. So when shifting right the negative number you cant "expect" anything

So it is just as it is in your implementation. It is not weird.

6.5.7/5 [...] If E1 has a signed type and a negative value, the resulting value is implementation- defined.

It may also invoke the UB

6.5.7/4 [...] If E1 has a signed type and nonnegative value, and E1×2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

answered on Stack Overflow Sep 14, 2018 by 0___________ • edited Sep 14, 2018 by 0___________
-3

As noted by @P__J__, the right shift is implementation-dependent, so you should not rely on it to be consistent on different platforms.

As for your specific test, which is on a single platform (possibly 32-bit Intel or another platform that uses two's complement 32-bit representation of integers), but still shows a different behavior:

GCC performs operations on literal constants using the highest precision available (usually 64-bit, but may be even more). Now, the statement x = 0x80000000 >> 3 will not be compiled into code that does right-shift at run time, instead the compiler figures out both operands are constant and folds them into x = 0x10000000. For GCC, the literal 0x80000000 is NOT a negative number. It is the positive integer 2^31.

On the other hand, x = 0x80000000 will store the value 2^31 into x, but the 32-bit storage cannot represent that as the positive integer 2^31 that you gave as an integer literal - the value is beyond the range representable by a 32-bit two's complement signed integer. The high-order bit ends up in the sign bit - so this is technically an overflow, though you don't get a warning or error. Then, when you use x >> 3, the operation is now performed at run-time (not by the compiler), with the 32-bit arithmetic - and it sees that as a negative number.

answered on Stack Overflow Sep 14, 2018 by Leo K

User contributions licensed under CC BY-SA 3.0