2009-12-07 2 views
22

В C или C++ сказано, что максимальное число, которое может иметь размер size_t (неподписанный тип данных int), совпадает с литьем -1 в этот тип данных. например, см. Invalid Value for size_tПочему unsigned int 0xFFFFFFFF равно int -1?

Почему?

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

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

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

+15

Читайте о дополнении «Два дополнения» в Википедии; это самый распространенный способ кодирования отрицательных чисел в двоичном формате. – Artelius

+7

http://en.wikipedia.org/wiki/Two%27s_complement – 2009-12-07 21:50:48

+1

Вы заметите, что, как и без знаковых чисел, добавление 1 к максимально возможному числу даст вам минимально возможное число. –

ответ

46

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

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

(size_t) (SIZE_MAX + 1)

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

Обратите внимание, что выше справедливо:

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

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

Edit: Для того, чтобы ответить на некоторые из комментариев:

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

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.

Если мы делаем:

long j = LONG_MAX; 
int i = j; 

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

+4

Проголосовало за то, что вы первый человек, который отметил, что '(size_t) -1' из-за арифметических правил, которые C задает для неподписанных чисел, а не из-за основного представления. (Кстати, 'SIZE_MAX' - это макрос). – caf

+0

Спасибо! Я не хотел использовать 'SIZE_MAX', потому что это не на C89, а также потому, что я пытался сделать общий вывод. Тем не менее, я думаю, я мог бы упомянуть об этом. –

+0

«даже если лежащая в основе машина не представляет числа в двух дополнениях» - учитывая, что дополнение двух является способом представления чисел с номерами, как это применимо к целым целым без знака? –

1

То есть two's complement кодировка.

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

25

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

Относительно того, почему это делается таким образом, Wikipedia говорит:

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

+5

P.S. Однажды я работал над своей машиной. Это было странно, с положительным нулем и отрицательным нулем. –

+6

Как и плавающая точка? * утки * – Goz

+0

Мне никогда не приходило в голову, что плавающая точка имеет отрицательный ноль, но я вижу, что вы правы: http://en.wikipedia.org/wiki/Signed_zero –

7

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

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

3

Two's complement очень приятно делать вычитание так же, как дополнение :)

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

    11111111 (255 or -1) 
    +00000001 (1) 
    --------- 
    100000000 (0 + 256) 
+0

превосходные и четкие иллюстрации. Благодаря! – Aadishri

-1

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

+0

Вы только что повторили наблюдение OP, и он спрашивает *, почему * битовый шаблон для int - это; а также * why * (unsigned int) -1 дает '0xFFFFFFFF' (который ваш ответ не отвечает четко). Кроме того, это противоречивые рекомендации, чтобы не рекомендовать использование unsigned ints. Они очень часто используются. Обе подписанные ints и unsigned ints имеют подводные камни. Мое мнение таково, что в unsigned ints меньше ловушек, чем в подписанных ints, поэтому я предпочитаю использовать их, если не знаю, что требуются отрицательные значения. –

Смежные вопросы