4
char byte = 0x80
if(byte == 0x80)
{
    cout << "This message never gets printed!";
}

The hexadecimal value 0x80 is equivalent in binary to 1000 0000, which clearly fits in a byte.

However, the compiler warns me about the line with the conditional:

warning: comparison is always false due to limited range of data type

Why is the result of the conditional false in this case?

Is 0x80 getting expanded in the conditional to something like 0x80000000?

Is it possible to use the == operator to check if a char equals 0x80?

Cory Klein
  • 51,188
  • 43
  • 183
  • 243
  • 2
    `char` can be signed or unsigned. – chris Jul 24 '13 at 20:55
  • Yes. But even if `0x80` resolves to a negative number, are we not just comparing whether the contents of the `char` are equal to the literal? If not, please explain! :) – Cory Klein Jul 24 '13 at 20:56
  • 4
    @CoryKlein: `0x80` is an `int`. – Jesse Good Jul 24 '13 at 20:58
  • This got me before. Very annoying. – Neil Kirk Jul 24 '13 at 23:41
  • What's even more annoying is that this comparison will actually return true on some architectures and false on others. So code that was working will suddenly break when compiled for a different platform. And in my case, the compiler issued no warning like you got. – Michael Nov 01 '13 at 19:27

2 Answers2

9

The problem is that char is, in the C and C++ standards, defined that it can be either a signed or an unsigned value, and that a char must have at least 8 bits. The architecture and compiler in question appears to use signed 8-bit char values.

This means that any value with the highest bit (bit 7) will be a negative value. So 0x80 as a char becomes -128 decimal.

The constant 0x80 is not a char value, it is an int value.

So when you compare -128 with 0x80 (128) they are not the same, and can never be, and the compiler figures this out and issues a warning.

There are a variety of ways to achieve this, here are a few possible scenarios:

First, we can cast either value to the type of the other:

if (((int)byte) & 0xff == 0x80)

or

if (byte == (char)0x80) 

Alternatively, we can make the constant into a char value, rather than an int value.

if (byte == '\200')     // Octal 200 = 0x80. 

or

if (byte == '\x80')

Alternatively, use an unsigned char byte = 0x80; - specifying that it's an unsigned char will ensure that it doesn't "flip to negative".

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • For completeness I'd add that you can also compile (under clang and gcc at least) with `-funsigned-char`, which will force all `char`s to be unsigned. – wds May 14 '14 at 14:53
0

The signed char type can contain values 0x7f to -0x80 for a total of 256 values. The unsigned char type can contain the value 0x80 as the maximum value is 0xff. It appears that the implementation of the char type that you are using is a signed char type.

Assigning 0x80 to a signed char forces a value overflow, making the value be -0x80. The signed char type cannot represent 0x80 to the system as it is always interpreted as -0x80. This is because the most significant bit is used as the sign bit.

Your comparison could be successful if you used a bitwise and: byte & 0x80. Note that the result of this operation does not imply equality in either direction, merely whether the most significant bit is set.

abiessu
  • 1,907
  • 1
  • 18
  • 16