I can THINK of why this wouldn't work, but I don't understand why many of the workarounds I've tried don't work. Below is an example of the code I'm trying to make work. The intent should be obvious, but compiling with GCC 7.4.0 for Windows 32 bit, Visual C 32 bit and Visual C 64 bit as well as the same compilers in C++ modes, all of them result in the same answer, so I'm sure it's not just a compiler bug.
The code is:
unsigned short usAlgo = 0x0001;
unsigned short usNotAlgo = ~usAlgo;
if ( usAlgo == ~usNotAlgo )
printf("Pass\n");
else
printf("Fail\n");
On all the compilers I've tried, this code prints "Fail". By a slight rearrangement to:
unsigned short usCheck = ~usNotAlgo;
if ( usAlgo == usCheck )
It prints "Pass". I would have thought the usCheck would get optimized out anyway, so why is this different?
I have tried all kinds of workarounds that don't work, with bit masking, parentheses, making them signed values, and such like:
if ( usAlgo == (~usNotAlgo) & 0xffff )
or
if ( (unsigned int)(usAlgo) == ~(unsigned int)(usNotAlgo) )
I think I've discovered that the first of those two fails because '==' has a higher order of precedence than '&', but I can't for the life of me understand why the simple:
if ( usAlgo == ~usNotAlgo )
fails.
Looking at the compiler output doesn't REALLY help, other than I can see the "real" comparison ends up being:
if( 0x00000001 == 0xFFFF0001 )
implying, the unsigned short (0xFFFE) was first promoted to an unsigned int (0x0000FFFE) and THEN negated. (That's why we thought making them signed might sign extend to 0xFFFFFFFE.
I obviously have the answer to how to fix this, but I need to understand WHY.
Any ideas?
[Edit: Grammar]
As you have noticed, usNotAlgo
was promoted to type int
before the ~
operator was applied. Generally speaking, anytime a type smaller than int
is used in an expression, it is first promoted to int
.
This is documented in section 6.3.1.1p2 of the C standard:
The following may be used in an expression wherever an
int
orunsigned int
may be used:
- An object or expression with an integer type (other than
int
orunsigned int
) whose integer conversion rank is less than or equal to the rank ofint
andunsigned int
.- A bit-field of type
_Bool
,int
,signed int
,orunsigned int
.If an
int
can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to anint
; otherwise, it is converted to anunsigned int
. These are called the integer promotions. All other types are unchanged by the integer promotions.
Section 6.5.3.3p4 regarding the ~
operator specifically says:
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 minusE
.
This can be fixed by casting the result back to unsigned short
to mask off the additional bits:
if ( usAlgo == (unsigned short)~usNotAlgo )
The problem is that by writing ~usNotAlgo in the if statement, it got promoted to an int value, and then because of comparison, usAlgo value got promoted to int as well. This is why you see if(0x00000001 == 0xFFFF0001)
output from the compiler (instead of expected if( 0x0001 == 0x0001 )
).
In order to fix it, cast ~usNotAlgo to unsigned short:
if (usAlgo == (unsigned short)~usNotAlgo) {code...}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With