Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Logical right shift in C with ~0 value

Tags:

c

I have a very simple code, in which logical shift works with ~0 value in strange way

As I know its related to the signed/unsigned data types

#include <stdio.h>

void printfbits(int x) {
    for (int i=7; i>=0;i--) {
        printf("%d", x>>i & 1);
    }
    printf("\n");
}

int main() {
    printfbits(~0>>1); 
}

I expect 0111111, not 1111111. I also tried, no success

printfbits(((unsigned int)~0)>>1);
like image 863
delkov Avatar asked Feb 03 '19 15:02

delkov


3 Answers

On most platforms, int is 32 bits or 64 bits long. Therefore, you are shifting more than 8 bits to the right, which leaves you with more than 8 bits enabled:

11...11111111 >> 1

becomes:

11...11111111 // if sign extension happens
01...11111111 // if not

As you see, regardless of whether sign extension happens or not, you will still see all 1s since you only print the lower 8 bits.

like image 195
Acorn Avatar answered Oct 26 '22 10:10

Acorn


Right shift of negative signed values is implementation defined. For gcc, a 1 is shifted in, otherwise a 0 is shifted in.

You were on the right track with the cast, but it doesn't help since the function still expects an int. You need to change the function to take an unsigned char and you have to mask out all but the lowest byte before performing the shift in the function call.

#include <stdio.h>

void printfbits(unsigned char x) {
    for (int i=7; i>=0;i--) {
        printf("%d", x>>i & 1);
    }
    printf("\n");
}

int main() {
    printfbits((~0u & 0xff)>>1);
}

Also, note the use of the U suffix on the constant 0. That gives the constant a type of unsigned int.

like image 25
dbush Avatar answered Oct 26 '22 09:10

dbush


The negation in ~0 will happen with type int. But even if you do ~(unsigned char)0, it'll still happen with type int because of implicit promotions. Consequently, you'll get extra more 1-bits on the left (int is usually 32 bits large (must be at least 16)). You can strip them by casting the bitnegation result to uint8_t.

I'd also recommend doing bit ops on unsigneds (0u rather than 0) as the semantics are better standardized for those.

#include <stdio.h>
#include <stdint.h>

void printfbits(int x) {
    for (int i=7; i>=0;i--) {
        printf("%d", x>>i & 1);
    }
    printf("\n");
}

int main() {
    printfbits( (uint8_t)~0u >>1 );  //prints 01111111
}
like image 23
PSkocik Avatar answered Oct 26 '22 10:10

PSkocik