Int в unsigned int в C и С#

Посмотрите на этот код на C:

int main()
{
    unsigned int y = 10;
    int x = -2;
    if (x > y)
        printf("x is greater");
    else
        printf("y is greater");
    return 0;
}
/*Output: x is greater.*/ 

Я понимаю, почему вывод x больше, потому что, когда компьютер сравнивает оба из них, x продвигается до целого числа без знака. Когда x продвигается до целых чисел без знака, -2 становится 65534, который определенно больше 10.

Но почему в С# эквивалентный код дает противоположный результат?

public static void Main(String[] args)
{
    uint y = 10;
    int x = -2;
    if (x > y)
    {
        Console.WriteLine("x is greater");
    }
    else
    {
        Console.WriteLine("y is greater");
    }
}
//Output: y is greater. 

Ответ 1

В С#, как uint, так и int до сравнения сравниваются с long.

Это описано в 4.1.5 Integral types спецификации языка С#:

Для двоичных операторов +, -, *,/,%, &,, ==,! =, > , <, >= и < =, операнды преобразуются в тип T, где T - первый из int, uint, long и ulong, который может полностью представлять все возможные значения обоих операндов. Затем операция выполняется с использованием точности типа T, а типом результата является T (или bool для реляционных операторов). Не допускается, чтобы один операнд имел длинный тип, а другой - тип ulong с бинарными операторами.

Так как long - это первый тип, который может полностью представлять все значения int и uint, переменные оба преобразуются в long, а затем сравниваются.

Ответ 2

В С# при сравнении между int и uint оба значения повышаются до длинных значений.

"В противном случае, если любой операнд имеет тип uint, а другой операнд имеет тип sbyte, short или int, оба операнда преобразуются в тип long."

http://msdn.microsoft.com/en-us/library/aa691330(v=vs.71).aspx

Ответ 3

C и С# имеют разные представления, для которых представляют собой интегральные типы. См. Мой ответ fooobar.com/info/84172/... для обсуждения вопроса C. В С# ли целые числа представляют числа или члены абстрактного алгебраического кольца, в какой-то мере определяется тем, включена или выключена "проверенная арифметика", но это просто контролирует, должны ли вычисления вне пределов вычислять исключения. В общем случае платформа .NET рассматривает все целочисленные типы как представляющие числа, и кроме того, что позволяет выполнять некоторые вычисления вне пределов, не бросая исключений, С# следует за ее руководством.

Если неподписанные типы представляют собой элементы алгебраического кольца, добавление, например, -5 к unsigned 2 должен давать значение без знака, которое при добавлении к 5 даст 2. Если они представляют числа, то добавление a -5 к unsigned 2 должно, если возможно, дать представление числа -3. Поскольку продвижение операндов до Int64 позволит, чтобы это произошло, что делает С#.

Кстати, мне не нравятся представления о том, что операторы (особенно реляционные операторы!) всегда должны работать, продвигая свои операнды к общему совместимому типу, должны возвращать результат этого типа и должны принимать, не выкрикивая любую комбинацию операторов, которые могут быть продвигается к общему типу. Учитывая float f; long l;, для сравнения f==l имеется как минимум три разумных значения [он мог бы использовать l для float, он мог бы отличать l и f до double, или он мог гарантировать, что f - это целое число, которое можно отличить до long, а при произведении равно l]. В качестве альтернативы, компилятор может просто отказаться от такого смешанного сравнения. Если бы у меня были druthers, компиляторам было бы предписано отдать операнды реляционным операторам, за исключением случаев, когда имелось только одно правдоподобное значение. Требование, чтобы вещи, которые неявно конвертируемы повсюду, должны быть непосредственно сопоставимы, ИМХО бесполезно.