This is probably not what your instructor wants to see, but it works and it does, unlike the ~ operator, not depend on the implementation (sign-magnitude, 1-complement or 2-complement) and does not cause UB. I post this solution since it meets the requirements but is ugly and slow, because i think the task is stupid (the task leads to functions which only work on systems which use 2-complement, i would bet the instructor presents a solution which only works on 2-complement systems).
#include <limits.h>
//Returns the negative value of n,
//n has to be positive. Negative values of n cause UB.
int makeNegative(int n)
{
int i=INT_MIN;
while(i+n) //continue till i+n is 0, no need for the < operator
{
i++;
}
return i;
}
If you want code that does not cause UB when n is negative, you can check for negative values of n:
#include <limits.h>
int isNegative(int n)
{
int i=INT_MIN;
while(i)
{
i++;
if(n==INT_MAX) //when n reaches INT_MAX, it was positive
{ //we need to check for it before increment
return 0; //increment a int of value INT_MAX would cause UB
}
n++;
if(!n) //n is 0 means n was negative before
{
return 1;
}
}
return 0;
}
//Returns the negative value of n i n is positive
//returns n in any other case
int makeNegative(int n)
{
if(isNegative(n))
{
return n;
}
int i=INT_MIN;
while(i+n) //continue till i+n is 0, no need for the < operator
{
i++;
}
return i;
}
2-Complement system
As others already pointed out, this is what your instructor probably wants to see: n=(~n)+1;. This only works when you are using 2-complement and it causes UB for INT_MIN.
Lets say we have the number 1 stored in the variable n. Now we want to negate n. Then we have to invert the variable, means that we flip every bit and then add +1. Here is an example with a int8_t.
Value | Bit pattern |
1 | 0b00000001 | n is set to 1
-2 | 0b11111110 | We inverted n
-1 | 0b11111111 | We added 1 to n
The advantage of a 2-complement system is, that we can use every possible bit pattern for a value and that we can use the same logic for signed and unsigned variables for most operations. The binary value of 0b11111111 is 0xFF==255 for a unsigned variable. The C standard requires that unsigned integers wrap around. If we store 0xFF in a uint8_t variable and add 1, the result can't be stored in a uint8_t and wraps around to 0. Means 0b11111111+1==0. As we saw before we can store the value -1 in a int8_t as the bit pattern 0b11111111. And here we also get 0b11111111+1==0 => -1+1==0.
One feature 2-Complement systems have is that there is one more negative value than positive values. INT8_MIN<-INT8_MAX. INT8_MIN has the bit pattern 0b10000000, if we invert it we get the bit pattern 0b01111111==INT8_MAX. When we now add 1, we get an overflow and a overflow causes UB for signed variables*. So the statement n=(~n)+1; can causes UB when n is signed. The same is true for n=-n, n=n*-1 and n=n/-1, since they all fail when n has the lowest possible value which we can not negate.
*When you tell the compiler to use wrap around for signed variables, (-fwrapv on gcc), it would result in INT8_MIN again.