Почему unsigned int 0xFFFFFFFF равно int -1?

В C или C ++ сказано, что максимальное число, которое может содержать size_t (беззнаковый тип данных int), совпадает с приведением -1 к этому типу данных. например, см. Недействительное значение для size_t

Почему?

Я имею в виду (говоря о 32-битных int) AFAIK самый значимый бит содержит знак в подписанном типе данных (то есть бит 0x80000000 для формирования отрицательного числа). тогда 1 равно 0x00000001 .. 0x7FFFFFFFF - это наибольшее положительное число, которое может содержать тип данных int.

Затем, AFAIK, двоичное представление -1 int должно быть 0x80000001 (возможно, я ошибаюсь). почему / как это двоичное значение преобразуется во что-то совершенно другое (0xFFFFFFFF) при преобразовании целочисленных значений в беззнаковые ?? или .. как можно сформировать двоичное -1 из 0xFFFFFFFF?

Я не сомневаюсь, что в C: ((unsigned int) -1) == 0xFFFFFFFF или ((int) 0xFFFFFFFF) == -1 одинаково верно, чем 1 + 1 == 2, мне просто интересно, почему.


person conejoroy    schedule 07.12.2009    source источник
comment
Прочтите о дополнении Two в Википедии; это наиболее распространенный способ кодирования отрицательных чисел в двоичном формате.   -  person Artelius    schedule 08.12.2009
comment
en.wikipedia.org/wiki/Two%27s_complement   -  person    schedule 08.12.2009
comment
Вы заметите, что, как и в случае с числами без знака, добавление 1 к максимально возможному числу даст вам наименьшее возможное число.   -  person Drew Dormann    schedule 08.12.2009
comment
Двоичное представление отрицательной единицы должно быть таким представлением, которое дает ноль при добавлении единицы. Это 0xFFFFFFFF.   -  person David Schwartz    schedule 24.07.2015


Ответы (6)


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

Для беззнаковых целочисленных типов (size_t является одним из них) стандарт C (и, я думаю, стандарт C ++ тоже) определяет точные правила переполнения. Короче говоря, если SIZE_MAX - максимальное значение типа size_t, то выражение

(size_t) (SIZE_MAX + 1)

гарантированно будет 0, и поэтому вы можете быть уверены, что (size_t) -1 равно SIZE_MAX. То же самое верно и для других беззнаковых типов.

Обратите внимание, что вышесказанное верно:

  • для всех беззнаковых типов,
  • даже если базовая машина не представляет числа в дополнении до двух. В этом случае компилятор должен убедиться, что идентичность верна.

Кроме того, вышеизложенное означает, что вы не можете полагаться на определенные представления для подписанных типов.

Изменить: чтобы ответить на некоторые комментарии:

Допустим, у нас есть такой фрагмент кода:

int i = -1;
long j = i;

В присвоении j есть преобразование типа. Предполагая, что int и long имеют разные размеры (в большинстве [всех?] 64-битных систем), битовые шаблоны в ячейках памяти для i и j будут разными, потому что они имеют разные размеры. Компилятор следит за тем, чтобы значения i и j равнялись -1.

Точно так же, когда мы делаем:

size_t s = (size_t) -1

Происходит преобразование типа. -1 относится к типу int. Он имеет битовый шаблон, но это не имеет отношения к этому примеру, потому что, когда преобразование в size_t происходит из-за приведения, компилятор преобразует значение в соответствии с правилами для типа (size_t в Это дело). Таким образом, даже если int и size_t имеют разные размеры, стандарт гарантирует, что значение, хранящееся в s выше, будет максимальным значением, которое может принимать size_t.

If we do:

long j = LONG_MAX;
int i = j;

Если LONG_MAX больше INT_MAX, тогда значение в i определяется реализацией (C89, раздел 3.2.1.2).

person Alok Singhal    schedule 07.12.2009
comment
Проголосовал, потому что вы первый человек, который заметил, что (size_t)-1 связано с арифметическими правилами, которые C определяет для чисел без знака, а не из-за лежащего в основе представления. (Кстати, SIZE_MAX - это макрос). - person caf; 08.12.2009
comment
Спасибо! Я не хотел использовать SIZE_MAX, потому что его нет в C89, а также потому, что я пытался сформулировать общую мысль. Тем не менее, думаю, я мог бы упомянуть об этом. - person Alok Singhal; 08.12.2009
comment
даже если базовая машина не представляет числа в дополнении до двух - учитывая, что дополнение до двух - это способ представления чисел со знаком, как это вообще можно применить к целым числам без знака? - person Pavel Minaev; 08.12.2009
comment
Если стандарт гарантирует, что SIZE_T_MAX + 1 == 0, переводится ли это автоматически в (size_t) -1 == SIZE_T_MAX? Я не уверен, что это так, особенно если компилятору нужно выдать специальный код для проверки этого случая. Я даже не уверен, что гарантирую (size_t) 0 - (size_t) 1 == SIZE_T_MAX. Может быть, в стандарте есть что сказать по этому поводу больше, чем вы предполагали, у меня его нет под рукой. - person Mark Ransom; 08.12.2009
comment
Отметка: Целые числа без знака должны подчиняться законам арифметики по модулю 2 ** n, где n - количество бит в представлении значения этого конкретного размера целого числа. [3.9.1 / 4, C ++ 03] - person ; 08.12.2009
comment
Отредактировал свой пост для разъяснения. Также см. Ответ Роджера. - person Alok Singhal; 08.12.2009
comment
@ Роджер, спасибо за это. Кажется, что (size_t) 0 - (size_t) 1 даст желаемый ответ. Однако все еще не уверен, что это применимо к (size_t) -1, поскольку -1 находится за пределами диапазона, обрабатываемого size_t. P.S. стоит ли после этого спорить об ангелах на булавочной головке? - person Mark Ransom; 08.12.2009
comment
@Alok, ваша редакция лучше ответила на мой вопрос. Проще говоря, независимо от внутреннего представления двоичного числа; это не имеет отношения к C и его правилам целочисленной арифметики. В заключение, учитывая, что существуют разные аппаратные представления для отрицательного целого числа, нет возможности манипулировать ими на битовом уровне в C, чтобы сделать отрицательные числа из их битов, это правильно? - person conejoroy; 08.12.2009
comment
@conejoroy: Это возможно, но нужно быть предельно осторожным, и решение, скорее всего, не будет переносимым. Во-первых, вам нужно знать размер базового объекта (легко, sizeof сделает это). Вам также необходимо знать порядок байтов машины (тоже довольно просто, хотя есть некоторые подводные камни - и могут быть системы с очень странным порядком байтов). Затем ваш компилятор может добавлять биты заполнения и / или иметь представления ловушек (битовые шаблоны, которые не имеют никакого смысла). Так что лучше этого не делать. - person Alok Singhal; 08.12.2009
comment
+1 за четкий ответ. Нравится, как вы подчеркиваете аспект ценности. - person Johannes Schaub - litb; 08.12.2009
comment
Что касается большинства (всех?) 32-битных систем, я бы сказал, что очень мало 32-битных систем. Обычно int и long в этих системах 32-битные. Вы получите несоответствие в 16-битных системах (16/32) или некоторых 64-битных системах (32/64). - person M.M; 24.07.2015
comment
@MattMcNabb приятно. Я, наверное, имел ввиду 64-битную вместо 32-битной. - person Alok Singhal; 24.07.2015

Это называется дополнением до двух. Чтобы получить отрицательное число, инвертируйте все биты, затем добавьте 1. Итак, чтобы преобразовать 1 в -1, инвертируйте его в 0xFFFFFFFE, затем добавьте 1, чтобы получить 0xFFFFFFFF.

Что касается того, почему это делается именно так, Википедия говорит:

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

person Mark Ransom    schedule 07.12.2009
comment
P.S. Однажды я работал на машине дополнения. Было странно иметь и положительный, и отрицательный ноль. - person Mark Ransom; 08.12.2009
comment
Мне никогда не приходило в голову, что с плавающей запятой отрицательный ноль, но я вижу, что вы правы: en.wikipedia. org / wiki / Signed_zero - person Mark Ransom; 08.12.2009
comment
В старой серии Control Data Cyber ​​использовалось одно дополнение. Машинный язык был странным: стандартная инструкция сравнения рассматривала бы +0 как больше, чем -0, стандартная инструкция равенства рассматривала бы их как неравные, но был ли это ноль? инструкция, которая по сути вернула истину для -0 и +0 и ложь для всего остального. К счастью, мне никогда особо не приходилось с этим сталкиваться. К счастью, мне никогда не приходилось выполнять низкоуровневую обработку текста, поскольку она умещала 6-битные символы 10 в машинное слово, а строчные буквы обрабатывались смешанным 6- и 12-битным представлением. - person David Thornley; 08.12.2009
comment
Ответ не меняется, даже если вы работаете на машине с дополнениями или любой другой странной машине. Подробности смотрите в моем ответе. Как мелочь, это дополнение для одного человека, а не его дополнение. От Кнута: Дополнительное число до двух дополняется до единственной степени двойки, а дополнительное число до единицы дополняется до длинной последовательности единиц. В самом деле, существует также запись дополнения до двоек, которая имеет основание 3 и дополнение по отношению к (2 ... 22) _3. - person Alok Singhal; 08.12.2009
comment
@David: Поздравляю с тем, что я угадал машину, на которой я впервые научился сборке. @Alok: Я не знал, что стандарт дает такие гарантии, я никогда не использовал C или C ++ на любой машине, которая не использовала два дополнения. Полагаю, они крайне редки. - person Mark Ransom; 08.12.2009
comment
@Mark Ransom: Возможно, правильнее сказать, сделай число отрицательным или даже поменяй знак числа, чем сделай отрицательное число. Пока установлен самый высокий бит, вы получили отрицательное число. - person Drew Dormann; 08.12.2009
comment
@David: в контрольных данных вы можете немного обмануть: если вы добавите ноль к числу, это превратит отрицательный ноль в положительный ноль, что сделало бы сравнения простыми и понятными. - person Jerry Coffin; 08.12.2009

Ваш первый вопрос о том, почему (unsigned)-1 дает максимально возможное значение без знака, случайно связан с двумя дополнениями. Причина, по которой приведение -1 к беззнаковому типу дает наибольшее возможное значение для этого типа, заключается в том, что стандарт говорит, что беззнаковые типы «подчиняются законам арифметики по модулю 2 n, где n - количество битов в представление значения этого конкретного размера целого числа ".

Теперь, для дополнения 2, представление максимально возможного значения без знака и -1 оказывается одинаковым, но даже если оборудование использует другое представление (например, дополнение до 1 или знак / величина), преобразование -1 в беззнаковый тип должно по-прежнему производят максимально возможное значение для этого типа.

person Jerry Coffin    schedule 07.12.2009

Дополнение до двух очень удобно для вычитания, как и для сложения :)

    11111110 (254 or -2)
   +00000001 (  1)
   ---------
    11111111 (255 or -1)

    11111111 (255 or -1) 
   +00000001 ( 1)
   ---------
   100000000 ( 0 + 256)
person pmg    schedule 07.12.2009

Это кодировка с дополнением до двух.

Главный бонус заключается в том, что вы получаете одинаковую кодировку независимо от того, используете ли вы беззнаковое или подписанное int. Если вы вычтите 1 из 0, целое число просто обернется. Следовательно, 1 меньше 0 - это 0xFFFFFFFF.

person Goz    schedule 07.12.2009

Поскольку битовый шаблон для int -1 - это FFFFFFFF в шестнадцатеричном формате без знака. 111111111111111111111111111111 двоичное без знака. Но в int первый бит означает, отрицательный ли он. Но в unsigned int первый бит - это просто дополнительное число, потому что unsigned int не может быть отрицательным. Таким образом, дополнительный бит позволяет беззнаковому типу int хранить большие числа. Как и в случае с беззнаковым int, 11111111111111111111111111111111 (двоичный) или FFFFFFFF (шестнадцатеричный) - это наибольшее число, которое может хранить uint. Беззнаковые Ints не рекомендуются, потому что, если они становятся отрицательными, они переполняются и переходят к наибольшему числу.

person trinalbadger587    schedule 24.07.2015
comment
Вы только что повторили наблюдение OP, и он спрашивает почему битовый шаблон для int таков; а также why (unsigned int) -1 дает 0xFFFFFFFF (на который ваш ответ не дает четкого ответа). Кроме того, это спорный совет не рекомендовать использовать целые числа без знака. Они очень часто используются. Как подписанные целые числа, так и неподписанные целые числа имеют подводные камни. Я считаю, что беззнаковые целые числа имеют меньше подводных камней, чем подписанные целые числа, поэтому я предпочитаю использовать их, если я не знаю, что требуются отрицательные значения. - person M.M; 24.07.2015