Значения без знака и со знаком в C (выход)

signed int x = -5;
unsigned int y = x;

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


person sambhav jain    schedule 08.09.2010    source источник
comment
Когда вы попробовали это, что вы увидели?   -  person S.Lott    schedule 08.09.2010
comment
@KennyTM, реализация не определена; приведение от беззнакового целого числа к подписанному целому, которое не может его представить, определяется реализацией, но наоборот (знаковое -> беззнаковое) четко определено.   -  person bdonlan    schedule 08.09.2010
comment
@bdonlan: UINT_MAX является реализацией.   -  person kennytm    schedule 08.09.2010
comment
Этот вопрос мне задают в интервью....   -  person sambhav jain    schedule 08.09.2010
comment
Не могли бы вы, ребята, предложить мне любую книгу или статью, из которой я могу изучить эту базовую концепцию управления хранением и преобразованием....   -  person sambhav jain    schedule 08.09.2010
comment
@sambhav jain: Получите компилятор C. Пишите небольшие программы. Проводить исследования.   -  person S.Lott    schedule 08.09.2010
comment
@KennyTM: пожалуйста, исправьте неверный комментарий. Я не могу поверить, что это получило +3 голоса. Есть админы, которые могут это исправить?   -  person R.. GitHub STOP HELPING ICE    schedule 08.09.2010
comment
@R.: Что в этом плохого? Это зависит от UINT_MAX. Мы знаем, что это как минимум 65535, но оно может быть и больше (что обычно и происходит в настоящее время), и оно определяется реализацией. Ответ: UINT_MAX - 4. Как это не определяется реализацией?   -  person David Thornley    schedule 08.09.2010
comment
@David: строго говоря, значение y определяется стандартом и зависит от чего-то, что определяется реализацией. Это совсем не то же самое, как если бы в стандарте говорилось, что значение y определяется реализацией, поэтому я не уверен, что определенная реализация является правильным ответом на вопрос, каково значение y?. Зависит от реализации, безусловно, было бы правдой, если бы это было бесполезно по сравнению с фактическим ответом KennyTM.   -  person Steve Jessop    schedule 08.09.2010
comment
@ S.Lott: как раз то, что нам нужно, больше людей, которые изучают C, предполагая, что все, что делает их компилятор, - это C, а не следование стандарту. C — это не Python, язык не определяется реализацией.   -  person Steve Jessop    schedule 08.09.2010
comment
@Steve Jessop: ответ, согласно стандарту, UINT_MAX - 4. UINT_MAX определяется реализацией. Есть прямой ответ, но числовой ответ определяется реализацией.   -  person David Thornley    schedule 08.09.2010
comment
@Steve Jessop: Из вопроса неясно, идет ли речь о стандарте или о реализации стандарта. Или речь идет о том, что поведение определяется реализацией? Вопросы нечеткие. Вы знаете, о чем вопрос? Почему изучение конкретной реализации — при столкновении с поведением, определяемым реализацией — плохо?   -  person S.Lott    schedule 08.09.2010
comment
@David: я думаю, что это вводящее в заблуждение определение поведения, определяемого реализацией. Реализации не определяют процесс преобразования знакового значения в беззнаковое, и именно это неправильно в комментарии KennyTM.   -  person Steve Jessop    schedule 08.09.2010
comment
@S.Lott: не имеет значения, идет ли речь о языке программирования C или о MSVC 5.0 (или о чем-то еще), поскольку различные полезные ответы и комментарии здесь обходятся без необходимости знать, какие . Конечно, было бы серьезной трагедией, если бы кто-то ушел, думая, что в C (что фигурирует в вопросе) результат равен 65531, потому что они попробовали это на одном компиляторе, и это то, что они получили. В любом случае, если кто-то задает вопрос, который, как он говорит, касается языка программирования C, кажется извращенным предполагать, что на самом деле их волнует только одна реализация.   -  person Steve Jessop    schedule 08.09.2010


Ответы (5)


Это зависит от максимального значения 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.

person kennytm    schedule 08.09.2010

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

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

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

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

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

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

person bdonlan    schedule 08.09.2010
comment
Вам не хватает одного: UINT_MAX равен 2 ^ N-1. - person Jens Gustedt; 08.09.2010
comment
Не совсем правильно. Правильная формула y = x + UINT_MAX - 1. - person AnT; 08.09.2010
comment
@AndreyT: На самом деле правильная формула y = x + UINT_MAX + 1. - person kennytm; 08.09.2010
comment
@Jens @KennyTM, действительно, исправлено - person bdonlan; 08.09.2010

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

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

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

person AnT    schedule 08.09.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 будет дополнено знаком перед преобразованием в 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;
person Andreas Magnusson    schedule 08.09.2010
comment
Но вопрос в преобразовании в тип unsigned. Тем не менее, в ответе вы упоминаете только преобразования в типы signed. - person AnT; 08.09.2010
comment
Я позволю себе не согласиться, хотя я несколько сосредотачиваюсь в своем ответе на преобразованиях в подписанные типы, поскольку именно там есть драконы. Я также не думаю, что мой ответ заслуживает отрицательного голоса, но я предвзят. - person Andreas Magnusson; 09.09.2010
comment
+1: для дополнения до двух, даже если на самом деле уклончивая формулировка C99 дает определение, которое также будет работать с компилятором, который будет использовать, скажем, BCD. Интересно, существует ли такой компилятор C в любом случае, пока не доказано обратное. Я считаю, что все существующие в настоящее время компиляторы C используют дополнение до двух. - person kriss; 09.09.2010

y=0xffffffffb это двоичное представление -5 (дополнение до двух)

person Philibert Perusse    schedule 08.09.2010
comment
Результат не зависит ни от какого бинарного представления. Результат диктуется требованиями языкового стандарта. И стандарт в этом случае довольно точен. - person AnT; 08.09.2010
comment
@АндрейТ. Справедливости ради, хотя это и не утверждается таким образом, безусловно верно, что преобразование дополнения 2 -> беззнаковое приводит к одному и тому же битовому шаблону (я думаю, что стандарт C++ упоминает об этом вскользь, не могу вспомнить, делает ли это стандарт C ). Описание Филибера эквивалентно определению в стандарте: вы можете преобразовать со знаком -> без знака, разработав представление знакового значения в дополнении до 2, а затем прочитав его как беззнаковое значение. Он никогда не говорил, что реализация делает это на самом деле (хотя, без сомнения, реализация дополнения 2 делает). - person Steve Jessop; 08.09.2010
comment
@Steve Jessop: Ну, это не эквивалентно определению в стандарте, по крайней мере, потому, что стандартное определение не ограничивается преобразованиями между типами одного размера, в то время как один и тот же подход к представлению применим только при одинаковых размерах. - person AnT; 08.09.2010
comment
@AndreyT: правда, это эквивалентно только тому делу, о котором спрашивают, оно вообще не касается других случаев. Меня больше беспокоит предположение, что int составляет 32 бита. - person Steve Jessop; 08.09.2010