Почему вычитание int.MaxValue из uint отличается от вычитания переменной типа int из uint? - PullRequest
0 голосов
/ 13 января 2019

По крайней мере, на первый взгляд, это не вопрос переполнения ни int, ни uint. Я понимаю, что значения перекрываются только на половину, а диапазоны равны. На самом деле я рассчитываю на это.

Справочная информация: Есть алгоритм хэширования DotNet CRC, который возвращает uint, и мне нужно преобразовать его для хранения в sql (в котором отсутствует uint). Это преобразование всегда должно быть возможным, так как общий диапазон обоих одинаков, даже если начальная и конечная точки различны. Нет беспокойства об обратном преобразовании. Я отладил это как DotNet 4.0 и 4.6.1 с теми же результатами.

Я сбит с толку здесь:

В примерах кода ниже:
intForSqlStorageOK успешно.
Но intForSqlStorageFail1 выдает исключение времени выполнения.
Чем они отличаются?


{
    uint uIntToBeStoredInSql = 591071; //CRC32 hash result
    int intMaxValue = int.MaxValue;
    int intForSqlStorageOK = Convert.ToInt32(uIntToBeStoredInSql - intMaxValue - 1);

    //System.OverflowException: 'Value was either too large or too small for an Int32.'
    int intForSqlStorageFail1 = Convert.ToInt32(uIntToBeStoredInSql - int.MaxValue - 1);
    int intForSqlStorageFail2 = Convert.ToInt32(uIntToBeStoredInSql - Convert.ToUInt32(int.MaxValue) - 1);
    int intForSqlStorageFail3 = checked(Convert.ToInt32(uIntToBeStoredInSql - int.MaxValue - 1));
    int intForSqlStorageFail4 = unchecked(Convert.ToInt32(uIntToBeStoredInSql - int.MaxValue - 1));
    int intForSqlStorageFail5; //??? probably many others ???

    ///this SO led to this workaround, which is just clobbering the problem by adding memory. doesn't explain the difference above.
    ///https://stackoverflow.com/questions/26386201/subtracting-uint-and-int-and-constant-folding
    int intForSqlStorageOK2 = Convert.ToInt32(Convert.ToInt64(uIntToBeStoredInSql) - int.MaxValue - 1);

    ///Thanks ckuri in comments - this is way more terse and somehow still arrives at same result
    int intForSqlStorageOK3 = (int)uIntToBeStoredInSql - intMaxValue - 1;
    int intForSqlStorageOK4 = (int)uIntToBeStoredInSql - int.MaxValue - 1;

}

Спасибо всем за комментарии! узнал много

1 Ответ

0 голосов
/ 13 января 2019

В соответствии со спецификацией , int.MaxValue может быть неявно преобразовано в тип uint, тогда как непостоянное выражение int не может, потому что во время компиляции компилятор не знает, имеет ли значение int выражение в пределах диапазона uint типа или нет.

Если вы примените правила для разрешения оператора, то вы увидите, что uIntToBeStoredInSql - intMaxValue - 1 интерпретируется как (long)uIntToBeStoredInSql - (long)intMaxValue - 1L с общим значением -2146892577L (которое находится в диапазоне типа int).

В то время как uIntToBeStoredInSql - int.MaxValue - 1 интерпретируется как uIntToBeStoredInSql - (uint)int.MaxValue - 1u с общим значением 2148074719u (которое не находится в диапазоне типа int) в контексте unchecked и OverflowException исключение в контексте checked.

...