if statement evaluating complex expressions

Hi fellow coders, I'm having trouble wrapping my mind around a confusing issue and need some clarification regarding how if statements are evaluating.

The following code works, but it shouldn't:

if ((preamble == 150) && ((curdata >> 16) == ~(curdata & 0xFFFF))) {
    return(curdata & 0xFFFF);

In my transmitter code, the integer to be transmitted is shifted out, followed by its inverse. Here is the original thread in the hardware forum. In that thread,

I really don't know how it works like this. If curdata == 0xAAAA5555 and you put it through

((curdata >> 16) == ~(curdata & 0xFFFF))

then (curdata >> 16) = 0x0000AAAA
and ~(curdata & 0xFFFF) = 0xFFFFAAAA

this is because curdata & 0xFFFF evaluates first giving 0x00005555, then the ~ is evaluated giving 0xFFFFAAAA.

I don't understand how that expression can ever evaluate to true. :frowning: If somebody knows that part of the puzzle please let me know! [smiley=huh.gif]

you might want to try something like this:

(((curdata >> 16) & 0xFFFF) == (~curdata & 0xFFFF))

Again this code shouldn't work, but does. It is returning (curdata & 0xFFFF) which should be the real integer's inverse. But, it is returning the actual number sent from the transmitter.

The following code still works like above, but is properly put together and reads correctly:

    if ((preamble == 150) && ((curdata >> 16) == (~curdata & 0xFFFF))) {
      return(curdata >> 16);

The two code snippets work the same, except the former returns (curdata & 0xFFFF) which really should be the inverse of the actual number, and the latter code returns it properly (curdata >> 16). Something about the way the if statement evaluates, is causing it to work when it shouldn't. It has something to do with ~(expression) changing the value held in curdata methinks.

Could someone please explain why the code above works like it does? Thanks In Advance.

The code as written should and does work because curdata is really two consecutive integers treated as a long, where the second integer is equal to the first integer with the bits inverted

(curdata >> 16) == ~(curdata & 0xFFFF)

means that the non inverted int value ie :
(curdata >> 16)
is the same as the second integer inverted
(curdata & 0xFFFF) is the second integer and the tilde ~ inverts this, which undoes the inversion performed in the send.

I hope that makes sense.

So, then I guess both
(curdata >> 16)
~(curdata & 0xFFFF)
result in unsigned ints that get compared? If they resulted in unsigned long then the first statement would evaluate as 0000nnnn and the second as FFFFnnnn, since the leading 0's would be inverted. This is obviously not the case.

Regardless, it is working right in the "fixed" version with (~curdata & 0xFFFF) which is good.

If you are interested in a peek at the assembler code the compiler produces here it is:

if ( ((curdata >> 16) == ~(curdata & 0xFFFF))) {
  e2:      29 81             ldd      r18, Y+1      ; 0x01  //load least significant byte of curdata
  e4:      3a 81             ldd      r19, Y+2      ; 0x02  // into working registers
  e6:      4b 81             ldd      r20, Y+3      ; 0x03
  e8:      5c 81             ldd      r21, Y+4      ; 0x04  // this is most significant byte 
  ea:      89 81             ldd      r24, Y+1      ; 0x01  // this loads a copy of curdata 
  ec:      9a 81             ldd      r25, Y+2      ; 0x02  //into another set of registers
  ee:      ab 81             ldd      r26, Y+3      ; 0x03
  f0:      bc 81             ldd      r27, Y+4      ; 0x04
  f2:      9a 01             movw      r18, r20             // move the upper two bytes to the lower (i.e. >>16)
  f4:      44 27             eor      r20, r20             // exclusive or upper bytes
  f6:      55 27             eor      r21, r21
  f8:      a0 70             andi      r26, 0x00      ; 0    // set the copy of upper bytes to 0
  fa:      b0 70             andi      r27, 0x00      ; 0
  fc:      80 95             com      r24                    // flip all the bits in the copy
  fe:      90 95             com      r25
 100:      a0 95             com      r26
 102:      b0 95             com      r27
 104:      28 17             cp      r18, r24                // compare the four bytes 
 106:      39 07             cpc      r19, r25
 108:      4a 07             cpc      r20, r26
 10a:      5b 07             cpc      r21, r27
 10c:      41 f4             brne      .+16           ; 0x11e <loop+0x60>  //branch if not equal

If curdata is defined as a signed integer then the the ">>" operator may be doing sign extension so that the MSB in 0xAAAA5555 is being shifted right to be 0xFFFFAAAA.