40

Following is the c# code:

   static void Main(string[] args)
    {
        uint y = 12;
        int x = -2;
        if (x > y)
            Console.WriteLine("x is greater");
        else
            Console.WriteLine("y is greater");
    }

and this is c++ code:

int _tmain(int argc, _TCHAR* argv[])
{
unsigned int y = 12;
int x = -2;
if(x>y)
    printf("x is greater");
else
    printf("y is greater");

return 0;
}

Both are giving different result. Am I missing something basic? Any idea?

Davide Piras
  • 43,984
  • 10
  • 98
  • 147
Samir Lakhani
  • 485
  • 6
  • 14
  • possible duplicate of [Signed/unsigned comparisons](http://stackoverflow.com/questions/5416414/signed-unsigned-comparisons) – user541686 Nov 25 '11 at 07:54
  • 4
    General tip: Always make sure both values are from the same type for a comparison – RvdK Nov 25 '11 at 07:56
  • @Mehrdad: That question is about why signed/unsigned comparisons in C++ cause a compiler warning when the operator is <, >, >=, or <=, but not when the operator is ==. That's an entirely different question and its answer has very little bearing on the answer to this one. – Keith Irwin Nov 25 '11 at 08:08

3 Answers3

48

C++ and C# are different languages. They have different rules for handling type promotion in the event of comparisons.

In C++ and C, they're usually compared as if they were both unsigned. This is called "unsigned preserving". C++ and C compilers traditionally use "unsigned preserving" and the use of this is specified in the C++ standard and in K&R.

In C#, they're both converted to signed longs and then compared. This is called "value preserving". C# specifies value preserving.

ANSI C also specifies value preserving, but only when dealing with shorts and chars. Shorts and chars (signed and unsigned) are upconverted to ints in a value-preserving manner and then compared. So if an unsigned short were compared to a signed short, the result would come out like the C# example. Any time a conversion to a larger size is done, it's done in a value-preserving manner, but if the two variables are the same size (and not shorts or chars) and either one is unsigned, then they get compared as unsigned quantities in ANSI C. There's a good discussion of the up and down sides of both approaches in the comp.lang.c FAQ.

Keith Irwin
  • 5,628
  • 22
  • 31
  • 1
    The C++ standard did provide a rule to handle this situation, and when `int` and `unsigned int` is involved, both operand would be converted to `unsigned int`. – fefe Nov 25 '11 at 08:17
  • 3
    "...they're usually compared as if they were both unsigned..." Every C or C++ compiler is required to convert the `int` to `unsigned int` in this case. This _is_ mandated by a formal standard, because an unsigned can hold a larger value than an int – Gunther Piez Nov 25 '11 at 08:57
  • Thanks for the comments. I doubled checked to see if you are correct, and as best I can tell, you are. So I've changed the answer. I also found more information about the ANSI C standard's conversions and how they work (value-preserving only when size increase conversions are done). So it's been updated to reflect that as well. – Keith Irwin Nov 25 '11 at 09:51
  • I removed the negative vote. The answer is much more precise know – Gunther Piez Nov 30 '11 at 13:56
  • The C behavior makes sense if one regards unsigned integer types as representing members of an abstract algebraic ring, rather than numeric quantities. Adding a number to an abstract ring yields another ring, somewhat like adding a number to a pointer yields a pointer (though--unlike pointers--ring elements can also be added to each other). – supercat Sep 13 '13 at 23:38
12

In C++, when you compare an unsigned int and a signed int, the signed int is converted to unsigned int. Converting a negative signed int to an unsigned int is done by adding UINT_MAX + 1, which is larger than 12 and hence the result.

In C#, if you are getting the opposite result then it means that in C# both the expressions are being converted to signed int signed long (long or System.Int64)1 and then compared.

In C++, your compiler must have given you the warning:

warning: comparison between signed and unsigned integer expressions

Rule:
Always take warnings emitted by the compiler seriously!

1 As rightly pointed out by svick in comments.

b4hand
  • 9,550
  • 4
  • 44
  • 49
Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • 4
    In C#, the expressions are converted to signed long (`long` or `System.Int64`). – svick Nov 25 '11 at 08:15
  • IIRC, there is no requirement for warning messages in the C++ standard, only error messages. "must" => "should"? – dalle Nov 25 '11 at 08:32
4

I don't know about the standard of C#, but in the C++ standard, usual arithmetic conversions would be applied to both operands of relational operators:

[......enum, floating point type involed......] 

— Otherwise, the integral promotions (4.5) shall be performed on both operands.
  Then the following rules shall be applied to the promoted operands:

    — If both operands have the same type, no further conversion is needed.

    — Otherwise, if both operands have signed integer types or both have
      unsigned integer types, the operand with the type of lesser integer
      conversion rank shall be converted to the type of the operand with
      greater rank.

    — Otherwise, if the operand that has unsigned integer type has rank
      greater than or equal to the rank of the type of the other operand, the
      operand with signed integer type shall be converted to  the type of the
      operand with unsigned integer type.

    — Otherwise, if the type of the operand with signed integer type can
      represent all of the values of the type of the operand with unsigned
      integer type, the operand with unsigned integer type shall be converted
      to the type of the operand with signed integer type.

    — Otherwise, both operands shall be converted to the unsigned integer type 
      corresponding to the type of the operand with signed integer type.

Thus, when unsigned int is compared with int, int would be converted to unsigned int, and -2 would become a very large number when converted to unsigned int.

fefe
  • 3,342
  • 2
  • 23
  • 45