Search code examples
cbit-manipulationbitwise-operators

Bitwise Negation Issue - C (Tiva C TM4C123GXL)


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.


Solution

  • 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).