2

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?

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
Milan Donhowe
  • 193
  • 1
  • 9
  • 3
    You're telling it to interpret the number as signed with "%d". Use "%u". – Lee Daniel Crocker Aug 05 '19 at 23:30
  • 1
    Actually that's undefined behaviour because of format specifier not matching type provided, additionally size doesn't match as well... Most likely, `uint8_t` would require `%hhu` format specifier; safer is to use the appropriate macros from [`` header](https://en.cppreference.com/w/c/types/integer). – Aconcagua Aug 05 '19 at 23:35
  • @LeeDanielCrocker: That will print 4294967294 or some similar value corresponding to the `unsigned int` width (`UINT_MAX-1`), not the 254 that the question seeks. – Eric Postpischil Aug 05 '19 at 23:51
  • 3
    @Aconcagua: There is no undefined behavior from the format specifier. The integer promotions automatically promote `x` to `int`, so the arguments `x` and `~x` both have type `int`, which is the proper type to format with `%d`. – Eric Postpischil Aug 05 '19 at 23:53
  • See [Implicit type promotion rules](https://stackoverflow.com/questions/46073295/implicit-type-promotion-rules). – Lundin Aug 06 '19 at 09:21
  • And btw even without the ~ causing integer promotion, variadic functions integrate promote their parameters anyway. So it is literally impossible to pass a `char` to printf. The best you can do is to tell printf to treat the passed `int` as `char`. – Lundin Aug 06 '19 at 09:23

1 Answers1

5

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);
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    This relies on the system using 2's complement for `int` . The result would be different for ones complement for example. You could avoid that dependency with `(uint8_t)~(unsigned)x` perhaps – M.M Aug 06 '19 at 01:09
  • @chux moot point for `uint8_t` since it is unsigned , conceivably the system could offer `int8_t` with some hoop-jumping for any operations involving that type. But yes, probably such a system never exists. – M.M Aug 06 '19 at 01:41