Home > database >  Bitwise Negation Issue - C (Tiva C TM4C123GXL)
Bitwise Negation Issue - C (Tiva C TM4C123GXL)

Time:03-25

While working with the TM4C123GXL I have ran across an odd behavior that I believe is to be attributed to the Compiler. (TI v20.2.5.LTS) The compiler seems to not properly follow order of operation when using the bitwise negation function in conjunction with the equivalence operator.

Essentially you will find that Option #1 will not work and will result in false even though it should be correct (from what I see). However Option #2 will work and will result in true. (From what I see this is the same just with a needless variable declaration)

Option #1 (Should work, but does not)

    uint8_t foo = 0x40; // 0100 0000
    uint8_t fooinv = 0xBF; // 1011 1111
    uint8_t bar = 0x04; // 0000 0100
    uint8_t barinv = 0xFB; // 1101 1111
    bool valid = true;
    valid = (foo == ~fooinv) && valid;
    valid = (bar == ~barinv) && valid;

Option #2 (Extra variable but works)

    uint8_t foo = 0x40; // 0100 0000
    uint8_t fooinv = 0xBF; // 1011 1111
    uint8_t bar = 0x04; // 0000 0100
    uint8_t barinv = 0xFB; // 1101 1111
    uint8_t temp1 = ~fooinv;
    uint8_t temp2 = ~barinv;
    bool valid = true;
    valid = (foo == temp1) && valid;
    valid = (bar == temp2) && valid;

I suspect that this is because there may be some kind of unresolved data hazard but I am unable to nail down what is going on here. I have yet to disassemble the code the compiler creates but any help is appreciated.

CodePudding user response:

The behavior of ~ is specified in C 2018 6.5.3.3 4, which includes:

… The integer promotions are performed on the operand, and the result has the promoted type…

The integer promotions convert uint8_t to int. Therefore, in ~fooinv, the value of fooinv, 0xBF, is converted to int. This does not change the value; it is still 0x000000BF, which is the same value, just with more bits shown. (For this answer, I will use a 32-bit int, as is currently common in C implementations.) Then performing bitwise negation yields 0xFFFFFF40. This differs from the value of foo, 0x40, so foo == ~fooinv of course yields false (zero).

If you wish to calculate what the bitwise negation of fooinv would be in a uint8_t, you can simply convert the result: (uint8_t) ~fooinv. The comparison foo == (uint8_t) ~fooinv yields true (one).

CodePudding user response:

The problem is that in these expressions

valid = (foo == ~fooinv) && valid;
valid = (bar == ~barinv) && valid;

there is used the integer promotions that converts operands like this ~fooinv to the type int. So actually you are dealing with the value = 0xFFFFFF40.

From the C Standard (6.5.3.3 Unary arithmetic operators)

4 The result of the ~ operator is the bitwise complement of its (promoted) operand (that is, each bit in the result is set if and only if the corresponding bit in the converted operand is not set). The integer promotions are performed on the operand, and the result has the promoted type. If the promoted type is an unsigned type, the expression ~E is equivalent to the maximum value representable in that type minus E.

So to get the expected result you should write

valid = (foo == ( uint8_t  )~fooinv) && valid;
valid = (bar == ( uint8_t  )~barinv) && valid;
  • Related