Milan Donhowe
Milan Donhowe

Reputation: 303

NOT bitwise operator breaks unsigned integers?

I am trying to have C interpret an inverted unsigned integer as an unsigned integer. How can I invert the bits of a unsigned number without C interpreting it as two’s complement?

Whenever I have an unsigned integer and I use it as a operand with the bitwise NOT operator it evaluates to a negative number as according to two’s complement. However by definition I am using an unsigned integer, a number that should not have a sign.

Below has my sandboxed code. I am currently using GCC version 9.1.1.

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

int main(){
   uint8_t x = 1;//0000 0001
   printf("NOT(%d) == %d", x, ~x);//1111 1110
}

I want the output of 254 (1111 1110) but C interprets the value as two’s complement so I get -2. Is there a way I can get C to not interpret the inverted byte as two’s complement?

Upvotes: 2

Views: 1531

Answers (1)

Eric Postpischil
Eric Postpischil

Reputation: 222323

In C, when an integer narrower than an int is used in an expression, it is automatically promoted to int. Thus, in ~x, the uint8_t is automatically converted to an int, and then the bitwise ~ is performed, and then the result is passed to printf as an int. So what is printed is the result of inverting all the bits of an int, instead of just the bits of a uint8_t.

To get a uint8_t result, cast the expression to uint8_t:

printf("NOT(%d) == %d", x, (uint8_t) ~x);

Note that after the uint8_t conversion is performed, the value is again automatically promoted to an int, so printf receives an int in either case. However, the conversion reduces the value to one representable in a uint8_t, and this is the value that will be printed.

However, the conversion to uint8_t is using an int as the source type, so the bits of ~x will be interpreted according to the implementation’s int format. To get the low bits of ~x without relying on the int format, you can extract them with & 0xff instead of using a cast:

printf("NOT(%d) == %d", x, ~x & 0xff);

Alternately, you can perform the bitwise ~ on an unsigned value instead of an int value:

printf("NOT(%d) == %d", x, (uint8_t) ~ (unsigned) x);

Upvotes: 5

Related Questions