Неподписанные и подписанные значения в C (вывод) - PullRequest
8 голосов
/ 08 сентября 2010
signed int x = -5;
unsigned int y = x;

Какое значение y? Как это так?

Ответы [ 5 ]

17 голосов
/ 08 сентября 2010

Зависит от максимального значения unsigned int.Как правило, unsigned int имеет длину 32 бита, поэтому UINT_MAX имеет значение 2 32 - 1. Стандарт C (§6.3.1.3 / 2) требует, чтобы преобразование со знаком → без знака выполнялось как

В противном случае, если новый тип является беззнаковым, значение преобразуется путем многократного сложения или вычитания значения, превышающего максимальное значение, которое может быть представлено в новом типе, до тех пор, пока значение не окажется в диапазоне новогоtype.

Таким образом, y = x + ((2 32 - 1) + 1) = 2 32 - 5 = 4294967291.


На платформе 2 в качестве дополнения , большинство реализаций которой в настоящее время , y также совпадает с представлением дополнения 2 x.

-5 = ~ 5 + 1 = 0xFFFFFFFA + 1 = 0xFFFFFFFB = 4294967291.

4 голосов
/ 08 сентября 2010

Из стандарта C99:

6.3.1.3 Целые числа со знаком и без знака

  1. Когда значение с целочисленным типом преобразуется в другой целочисленный тип, отличный от _Bool, еслизначение может быть представлено новым типом, оно не изменяется.
  2. В противном случае, если новый тип не подписан, значение преобразуется путем многократного сложения или вычитания на единицу больше максимального значения, которое может быть представлено вновый тип, пока значение не окажется в диапазоне нового типа.49)

49) Правила описывают арифметику по математическому значению, а не по значению выражения определенного типа.

Таким образом, вы будете эффективно рассматривать, y = x + UINT_MAX + 1.

Это просто означает, что представление с двойным дополнением используется без изменений как целое число без знака, что делает это очень быстрым на большинстве современных компьютеров, так как они используют двойное дополнение для целых чисел со знаком.

3 голосов
/ 08 сентября 2010

Значение y равно UINT_MAX - 5 + 1, т.е. UINT_MAX - 4.

При преобразовании целочисленного значения со знаком в тип без знака значение уменьшается по модулю 2 ^ N, где N - число формирующих значение битов в типе без знака. Это относится как к отрицательным, так и к положительным значениям со знаком.

Если вы конвертируете тип со знаком в тип без знака того же размера, это означает, что положительные значения со знаком остаются неизменными (например, +5 преобразуется в 5) и отрицательные значения добавляются в MAX + 1 где MAX - максимальное значение типа без знака (-5 преобразуется в MAX + 1 - 5).

1 голос
/ 08 сентября 2010

Знаковые значения обычно хранятся в виде чего-то, называемого дополнением до двух :

Числа дополнения до двух являются способом кодирования отрицательных чисел в обычный двоичный код, так что сложение все еще работает.Добавление -1 + 1 должно равняться 0, но обычное сложение дает результат 2 или -2, если только операция не принимает специального знака о знаковом бите и вместо этого не выполняет вычитание.Два дополнения приводят к правильной сумме без этого дополнительного шага.

Это означает, что фактическое представление чисел -5 и 4294967291 в памяти (для 32-битного слова) идентично, например: 0xFFFFFFFB или 0b11111111111111111111111111111011.Поэтому, когда вы делаете:

unsigned int y = x;

Содержимое x копируется дословно, то есть поразрядно в y.Это означает, что если вы проверяете необработанные значения в памяти x и y, они будут идентичны.Однако если вы сделаете:

unsigned long long y1 = x;

, значение x будет расширено до знака перед преобразованием в длинный без знака.В общем случае, когда 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;
0 голосов
/ 08 сентября 2010

у = 0xfffffffb это двоичное представление -5 (дополнение к двум)

...