Is there a simple way to do logical right shift in c for negative numbers like how we have >> for arithmetic right shift?
-
That supposed duplicate doesn't answer the question "is there a way to do logical right shift for negative numbers". Voting to re-open. – Lundin Oct 04 '16 at 15:08
-
Problem is, currently the only way to do logical right shift reliably is to do it on a unsigned type, but signed to unsigned conversion only preserves the bit pattern when the signed type is 2's complement format. So either you have a solution that relies on implementation defined behavior (one that assumes 2's complement for signed int) or use `intXX_t/uintXX_t` to dictate it. – user3528438 Oct 04 '16 at 15:32
2 Answers
Right-shifting negative numbers invoke implementation-defined behavior in C. What will happen is not specified by the standard, but left to the compiler to specify. So it can either result in an arithmetic shift, or a logical shift, or perhaps something else entirely (like rotation, though I've never heard of that). You can't know or assume which method that applies on your compiler, unless you read the specific compiler documentation.
Unsigned numbers always use logical shift however. So if you want logical shift on a negative number, portably, then convert to unsigned before the shift:
int32_t i = -1;
i = (int32_t) ((uint32_t)i >> n); // guaranteed to give logical shift
Similarly, if you want to guarantee an arithmetic shift:
int32_t i = -1;
bool negative = i < 0;
if(negative)
{
i = -i;
}
i = (int32_t) ((uint32_t)i >> n);
if(negative)
{
i = -i;
}

- 195,001
- 40
- 254
- 396
-
Hmm Using `int32_t/uint32_t` does give the guaranteed to give logical shift, but, though common, these types are optional and so restrict portability to a degree. The 2nd "guarantee an arithmetic shift" is UB for -2147483648 due to the `i = -i;`, yet likely works as desired. – chux - Reinstate Monica Oct 04 '16 at 13:53
-
@chux I wouldn't be concerned about compilers not supporting stdint.h. Such compilers need to be removed from the market asap, if we can help by writing code that relies on stdint.h, then all the better. What the standard says is irrelevant, programmers should demand that compilers are useful tools suitable for the real world. – Lundin Oct 04 '16 at 14:52
-
@chux The case of the variable having value INT_MIN and getting converted is not UB, but merely implementation-defined, 6.3.1.3/3: "Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised." – Lundin Oct 04 '16 at 14:52
-
1My comment was not `INT_MIN` being converted that invokes UB, but the `-` operation which is `int` overflow - which is UB. I agree with writing code that relies on int8_t, 16,32, 64, yet recognize it does limit portability in select cases - and maybe for the better. – chux - Reinstate Monica Oct 04 '16 at 15:21
Logical right shifting is done by casting the int
value to unsigned
before shifting:
int lsr(int n, int shift) {
return (int)((unsigned)n >> shift);
}
Arithmetic right shifting cannot be dpne diretly with the >>
operator on negative values in C as its effects are implementation defined. Here is a simple alternative without tests that replicates the sign bit for all values on 2s complement architectures:
int asr(int n, int shift) {
unsigned u = (unsigned)n;
return (int)((u >> shift) | ~(((u & (unsigned)INT_MIN) >> shift) - 1));
}
And here is a simple test program:
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
int lsr(int n, int shift) {
return (int)((unsigned)n >> shift);
}
int asr(int n, int shift) {
unsigned u = (unsigned)n;
return (int)((u >> shift) | ~(((u & (unsigned)INT_MIN) >> shift) - 1));
}
int main(int argc, char *argv[]) {
int n = (argc < 2) ? -2 : strtol(argv[1], NULL, 0);
int shift = (argc < 3) ? 2 : strtol(argv[2], NULL, 0);
printf("%d >> %d = %d\n", n, shift, asr(n, shift));
printf("%d >>> %d = %d\n", n, shift, lsr(n, shift));
return 0;
}

- 131,814
- 10
- 121
- 189