4

It seems so strange. I found misunderstanding. I use gcc with char as signed char. I always thought that in comparison expressions(and other expressions) signed value converts to unsigned if necessary.

int a = -4;
unsigned int b = a;
std::cout << (b == a) << std::endl; // writes 1, Ok

but the problem is that

char a = -4;
unsigned char b = a;
std::cout << (b == a) << std::endl; // writes 0

what is the magic in comparison operator if it's not just bitwise?

Bikineev
  • 1,685
  • 15
  • 20
  • 5
    First one outputs 1, are you sure? [Gcc disagrees](http://coliru.stacked-crooked.com/a/aeac0b4860185e10). – jrok Jan 09 '14 at 17:42
  • Neither the first nor the second one outputs `1`. – Shoe Jan 09 '14 at 17:43
  • Yes, the first one outputs 1 and the second outputs 0. Both are results of default promotions. – R.. GitHub STOP HELPING ICE Jan 09 '14 at 17:44
  • 2
    @R.., again, [no, they don't](http://coliru.stacked-crooked.com/a/2b47aa67c5522577). – Shoe Jan 09 '14 at 17:44
  • Oh, sorry, I missed the second `-` sign. – R.. GitHub STOP HELPING ICE Jan 09 '14 at 17:45
  • [Mandatory watching](http://channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Core-C-/Stephan-T-Lavavej-Core-C-7-of-n). – Kerrek SB Jan 09 '14 at 17:46
  • `what is the magic in comparison operator if it's not just bitwise?` If it were just a bitwise comparison then there wouldn't be much point in having both signed and unsigned types. – Lightness Races in Orbit Jan 09 '14 at 17:52
  • You should note that unlike int which is equivelent to signed int, char is NOT signed char, it's a distinct type from signed char and unsigned char. – AlexDan Jan 09 '14 at 18:05
  • 1
    Didn't you mean `unsigned xxx b = a;` for both cases? It would explain the confusion. – Marian Jan 09 '14 at 18:08
  • You say you "use gcc with char as signed char". Just to be clear, `char`, `signed char`, and `unsigned char` are always three distinct types. `char` always has the same size, signedness, and representation as *either* `signed char` *or* `unsigned char`; the implementation defines which one it is. – Keith Thompson Jan 09 '14 at 18:41

3 Answers3

3

According to the C++ Standard

6 If both operands are of arithmetic or enumeration type, the usual arithmetic conversions are performed on both operands; each of the operators shall yield true if the specified relationship is true and false if it is false.

So in this expression

b == a

of the example

char a = -4;
unsigned char b = -a;
std::cout << (b == a) << std::endl; // writes 0

the both operands are converted to type int. As the result signed char propagets its signed bit and two values become unequal.

To demonstrate the effect try to run this simple example

{
    char a = -4;
    unsigned char b = -a;

    std::cout << std::hex << "a = " << ( int )a << "'\tb = " << ( int )b << std::endl;

    if ( b > a ) std::cout << "b is greater than a, that is b is positive and a is negative\n";
}

The output is

a = fffffffc'   'b = 4
b is greater than a, that is b is positive and a is negative

Edit: Only now I have seen that definitions of the variables have to look as

    char a = -4;
    unsigned char b = a;

that is the minus in the definition of b ahould not be present.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • No, no (well, not in the first example at least). – jrok Jan 09 '14 at 17:48
  • 3
    The quote is incomplete. If you compare an unsigned and a signed type of the same rank and "less" than `int`, the signed operand is converted to unsigned type. – jrok Jan 09 '14 at 17:53
  • @jrok, can you cite the standard on that? – Shoe Jan 09 '14 at 17:53
  • @ jrok It seems it was not clear from my post what code snippet I was considering. I uodated my post that it would be more clear. – Vlad from Moscow Jan 09 '14 at 17:55
  • @Jefffrey To be honest, I don't feel like it, sorry :) But the quote applies when you compare unsigned char and signed char. In that case, yes, they are promoted to int. – jrok Jan 09 '14 at 17:55
  • 2
    @Jefffrey: Read `[C++11: 5/9]` – Lightness Races in Orbit Jan 09 '14 at 17:56
  • @ jrok In the quote there is written clear that it is applied to arithmetic types. – Vlad from Moscow Jan 09 '14 at 17:57
  • @Vlad: Nobody has disputed that. – Lightness Races in Orbit Jan 09 '14 at 18:08
  • Thank you, I made mistake when wrote unsigned char b = -a; But implicit conversion to int is the answer. Thank you! – Bikineev Jan 09 '14 at 18:19
  • @Bikineev It is funny that except me nobody saw this typo.:) – Vlad from Moscow Jan 09 '14 at 18:23
  • 2
    `char`, `signed char`, and `unsigned char` are almost always promoted to (signed) `int` by the "usual arithmetic conversions". But if `CHAR_MAX > INT_MAX` or `UCHAR_MAX > INT_MAX`, then `char` or respectively `unsigned char` will instead be promoted to `unsigned int`. This can happen only if `CHAR_BIT >= 16` and `sizeof (int) == 1` -- and, for plain `char`, if `char` happens to be an unsigned type. You'll never encounter this on a system with 8-bit bytes. – Keith Thompson Jan 09 '14 at 18:46
  • @ Keith Thompson, It is very unusual case that sizeof( int ) == sizeof( short ) == sizeof( char ) == 1. I am not even sure that the C++ Standard allows this and that CHAR_MAX can be gretaer than INT_MAX. At keast this contradicts the notion of rank. – Vlad from Moscow Jan 09 '14 at 18:50
  • 1
    @VladfromMoscow: I'm certain it's allowed in C; I'm nearly certain it's allowed in C++. Consider `CHAR_BIT == 16`, plain `char` is unsigned, `CHAR_MAX == 65536`, `sizeof (int) == 1`, `INT_MAX == 32767`. I'd be interested in seeing a clause in the standard that this would violate. (Similarly, `UINT_MAX > LONG_MAX` isn't even particularly unusual; `char` is unusual in that it can be either signed or unsigned.) BTW, if you want to notify me in a comment, you can't have a space between the at-sign and my name. – Keith Thompson Jan 09 '14 at 19:08
  • @Keith Thompson I am not sure neither relative to C nor to C++. This contradicts to the paragraph of the C++ Standard (and there is the same paragraph in the C Standard) "The rank of a signed integer type shall be greater than the rank of any signed integer type with a smaller size." So the usual arithmetic conversion has no any sense. At keast there is no need to convert to int. – Vlad from Moscow Jan 09 '14 at 19:53
  • @VladfromMoscow: Can you be more explicit about the contradiction you see? I don't see it. Note that in the case I'm talking about, `char` is an *unsigned* type, so the sentence you quoted doesn't apply. – Keith Thompson Jan 09 '14 at 19:55
  • @ Keith Thompson Now char has a rank higher than int. So there is no any sense in the integer promotion. – Vlad from Moscow Jan 09 '14 at 20:02
3

Since an (unsigned) int is at least 16 bits wide, let's use that for instructional purposes:

In the first case: a = 0xfffc, and b = (unsigned int) (a) = 0xfffc

Following the arithmetic conversion rules, the comparison is evaluated as:

((unsigned int) b == (unsigned int) a) or (0xfffc == 0xfffc), which is (1)


In the 2nd case: a = 0xfc, and b = (unsigned char) ((int) a) or:

b = (unsigned char) (0xfffc) = 0xfc i.e., sign-extended to (int) and truncated

Since and int can represent the range of both the signed char and unsigned char types, the comparison is evaluated as: (zero-extended vs. sign-extended)

((int) b == (int) a) or (0x00fc == 0xfffc), which is (0).


Note: The C and C++ integer conversion rules behave the same way in these cases. Of course, I'm assuming that the char types are 8 bit, which is typical, but only the minimum required.

Brett Hale
  • 21,653
  • 2
  • 61
  • 90
0

They both output 0 because unsigned values can get converted to signed values, not viceversa (like you said).

Shoe
  • 74,840
  • 36
  • 166
  • 272