You just need to take the two's complement
x = -x;
That works regardless of x is signed or unsigned
Why? Because what you're doing is essentially a quick way to convert a number to it's 2's complement
A shortcut to manually convert a binary number into its two's complement is to start at the least significant bit (LSB), and copy all the zeros (working from LSB toward the most significant bit) until the first 1 is reached; then copy that 1, and flip all the remaining bits
https://en.wikipedia.org/wiki/Two%27s_complement#Working_from_LSB_towards_MSB
You have only one bit set, so when you copy all the zero bits and invert the remaining zero bits you get its 2's complement. You can see it in your example: 00001000 = 8, 11111000 = -8
. Some other examples:
00010000 = 16, 11110000 = -16
00100000 = 32, 11100000 = -32
01000000 = 64, 11000000 = -64
If x is a signed type then it's easy to understand. In case of unsigned types then there are obviously no negative values, so it works based on how the C standard defined unsigned operations
A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.
which is just another definition of 2's complement, because one greater than the largest value that can be represented by the resulting type (i.e. UINTN_MAX + 1
in this case) is 2N. Negating an unsigned value always produce its two's complement in C even when the signed type is sign-magnitude or one's complement
Of course you can also cast it to a signed type x = -(int32_t)x;
if you want to type more. You have another solution as well
x = ~x + 1; // by 2's complement definition
An easy to understand solution
while (!(x & 0x80000000))
x |= x << 1;
This code doesn't need to loop constantly 32 times all the time as many of the solutions above