signed int x = -5;
unsigned int y = x;
Каково значение y
? Как это так?
signed int x = -5;
unsigned int y = x;
Каково значение y
? Как это так?
Это зависит от максимального значения unsigned int
. Обычно unsigned int
имеет длину 32 бита, поэтому UINT_MAX
равно 232 1. Стандарт C (6.3.1.3/2) требует, чтобы преобразование со знаком и без знака выполнялось как
В противном случае, если новый тип не имеет знака, значение преобразуется путем многократного добавления или вычитания на единицу больше, чем максимальное значение, которое может быть представлено в новом типе, пока значение не окажется в диапазоне нового типа.
Таким образом, y = x + ((232 1) + 1) = 232 5 = 4294967291.
На платформе дополнения 2, которой в настоящее время является большинство реализаций, y
также то же, что и представление x
в дополнении к 2.
-5 = ~5 + 1 = 0xFFFFFFFA + 1 = 0xFFFFFFFFB = 4294967291.
Из стандарта C99:
6.3.1.3 Целые числа со знаком и без знака
- Когда значение с целочисленным типом преобразуется в другой целочисленный тип, отличный от _Bool, если значение может быть представлено новым типом, оно не изменяется.
- В противном случае, если новый тип не имеет знака, значение преобразуется путем многократного добавления или вычитания на единицу больше, чем максимальное значение, которое может быть представлено в новом типе, пока значение не окажется в диапазоне нового типа. 49)
49) Правила описывают арифметику по математическому значению, а не по значению данного типа выражения.
Таким образом, вы будете рассматривать, фактически, y = x + UINT_MAX + 1
.
Это просто означает, что представление с дополнением до двух используется без изменений как целое число без знака, что делает это очень быстрым на большинстве современных компьютеров, поскольку они используют дополнение до двух для целых чисел со знаком.
y = x + UINT_MAX - 1
.
- person AnT; 08.09.2010
y = x + UINT_MAX + 1
.
- person kennytm; 08.09.2010
Значение y
равно UINT_MAX - 5 + 1
, то есть UINT_MAX - 4
.
При преобразовании целочисленного значения со знаком в беззнаковый тип значение уменьшается по модулю 2^N, где N — количество битов, формирующих значение, в беззнаковом типе. Это относится как к отрицательным, так и к положительным значениям со знаком.
Если вы конвертируете тип со знаком в тип без знака того же размера, вышеприведенное означает, что положительные значения со знаком остаются неизменными (например, +5
преобразуется в 5
), а отрицательные значения добавляются к MAX + 1
, где MAX
— максимальное значение беззнаковый тип (-5
преобразуется в MAX + 1 - 5
).
Значения со знаком обычно хранятся в виде дополнения до двух:
Дополнительные числа до двух — это способ кодирования отрицательных чисел в обычные двоичные числа, так что сложение все еще работает. Сложение -1 + 1 должно равняться 0, но обычное сложение дает результат 2 или -2, если только операция не обращает особое внимание на знаковый бит и вместо этого не выполняет вычитание. Дополнение до двух приводит к правильной сумме без этого дополнительного шага.
Это означает, что фактическое представление чисел -5 и 4294967291 в памяти (для 32-битного слова) идентично, например: 0xFFFFFFFB
или 0b11111111111111111111111111111011
. Итак, когда вы делаете:
unsigned int y = x;
Содержимое x копируется дословно, то есть побитно в y
. Это означает, что если вы проверите необработанные значения в памяти x
и y
, они будут идентичными. Однако, если вы сделаете:
unsigned long long y1 = x;
значение x
будет дополнено знаком перед преобразованием в unsigned long long. В общем случае, когда long long составляет 64 бита, это означает, что y1
равно 0xFFFFFFFFFFFFFFFB
.
Важно отметить, что происходит при приведении к более крупному типу. Значение со знаком, преобразованное в большее значение со знаком, будет дополнено знаком. Этого не произойдет, если исходное значение не имеет знака, например:
unsigned int z = y + 5;
long long z1 = (long long)x + 5; // sign extended since x is signed
long long z2 = (long long)y + 5; // not sign extended since y is unsigned
z
и z1
будут равны 0, а z2
— нет. Это можно исправить, приведя значение к подписанному перед расширением:
long long z3 = (long long)(signed int)y + 5;
или аналогично, если вы не хотите использовать расширение знака:
long long z4 = (long long)(unsigned int)x;
y=0xffffffffb это двоичное представление -5 (дополнение до двух)
int
составляет 32 бита.
- person Steve Jessop; 08.09.2010
UINT_MAX
является реализацией. - person kennytm   schedule 08.09.2010