Операторы побитового сдвига для типов со знаком

Я пытаюсь понять поведение побитовых операторов для подписанных и беззнаковых типов. В соответствии с документом ISO / IEC, я понимаю следующее.

Оператор левой смены

  • Результат E1 << E2, E1 сдвинутые влево битовые позиции E2

  • Освободившиеся биты за счет сдвига влево будут заполнены нулями.

  • E1 как неотрицательный со знаком: E1 << E2 приведет к E1, умноженному на 2 степени E2, если значение может быть представлено типом результата.

  • Q1. А как насчет подписанных негативов?

  • Q2: Я не могу понять, что подразумевается под «приведенным по модулю» в следующем контексте. «Если E1 имеет беззнаковый тип, значение результата будет E1 × 2E2, уменьшенное на единицу больше, чем максимальное значение, представленное в типе результата».

Оператор правого сдвига

  • Результат E1 >> E2 - E1 сдвинутые вправо битовые позиции E2.

  • E1 как неотрицательное со знаком / без знака: значение результата является неотъемлемой частью частного E1 / 2E2.

  • Q3: Я вижу, что для отрицательных целых чисел со знаком в некоторых книгах указывается, что вакантные должности будут заполняться 1. Пожалуйста, расскажите подробнее об использовании оператора сдвига вправо для отрицательного числа со знаком.


person Vivek Maran    schedule 24.11.2012    source источник


Ответы (4)


Q1: Поведение оператора сдвига влево для отрицательных значений целочисленных типов со знаком не определено, как и поведение для положительных значений целочисленных типов со знаком, когда результат E1 * 2^E2 не может быть представлен в типе.

Это прямо упоминается в разделах 6.5.7, параграфах 4 и 5 стандарта (проект n1570):

4 Результатом E1 << E2 является E1 сдвиг влево E2 битовых позиций; освобожденные биты заполняются нулями. Если E1 имеет беззнаковый тип, значение результата будет E1 × 2^E2, уменьшенное по модулю на единицу больше, чем максимальное значение, представленное в типе результата. Если E1 имеет тип со знаком и неотрицательное значение, и E1 × 2^E2 могут быть представлены в типе результата, то это результирующее значение; в противном случае поведение не определено.

Q2: уменьшение по модулю на единицу больше максимального значения, представленного в целочисленном беззнаковом типе, означает, что биты, которые сдвинуты слева, просто отбрасываются.

Математически, если максимальное значение беззнакового типа равно 2^n - 1 (и оно всегда имеет такую ​​форму), результатом сдвига E1 влево на E2 бит будет значение V в диапазоне от 0 до 2^n - 1, так что разница

(E1 * 2^E2 - V)

делится на 2^n, то есть это остаток, который вы получите при делении E1 * 2^E2 на 2^n.

Q3: Поведение при сдвиге вправо отрицательных значений целочисленных типов со знаком определяется реализацией. Наиболее распространенным поведением (по крайней мере, на двух дополнительных машинах) является арифметический сдвиг, то есть результатом является округление частного в меньшую сторону (в сторону отрицательной бесконечности).

5 Результатом E1 >> E2 является E1 сдвиг вправо E2 битовых позиций. Если E1 имеет беззнаковый тип или E1 имеет знаковый тип и неотрицательное значение, значение результата является неотъемлемой частью частного E1 / 2^E2. Если E1 имеет тип со знаком и отрицательное значение, результирующее значение определяется реализацией.

person Daniel Fischer    schedule 24.11.2012
comment
В C89 поведение отрицательных значений со смещением влево было определено точно для всех платформ, где беззнаковые типы имеют ровно на один бит данных больше, чем подписанные типы, а самый старший бит в беззнаковом типе находится в том же месте, что и знаковый бит подписанного типы; поведение на другой платформе фактически определялось реализацией. C99 изменил это на Undefined Behavior, но в обосновании этого изменения не упоминается, а тем более не предлагает никаких причин для него. Вопрос о том, определено ли это таким образом, зависит от того, используется ли платформа, совместимая с C89, для обычной цели. - person supercat; 08.12.2017

  • Re: Q1
    Если E1 отрицательное, поведение не определено.

  • Re: Q2
    Беззнаковая арифметика является "циклической", то есть она повторяется, так что UINT_MAX + 1 снова 0. Это как если бы все вычисления производились по модулю UINT_MAX + 1. Другой способ думать об этом - просто отбрасывать лишние биты, которые не помещаются слева.

  • Re: Q3
    Если E1 отрицательно, результат определяется реализацией. То есть, это зависит от вашей машины / компилятора / опций, но поведение должно быть где-то задокументировано («определено»), обычно в руководстве по компилятору. Два популярных варианта - заполнить входящие слева биты 1 (арифметический сдвиг) или 0 (логический сдвиг).

person melpomene    schedule 24.11.2012

Если вы действительно хотите понять операторы побитового сдвига. Взгляните на эти простые правила:

1) При сдвиге влево, E1 ‹< E2, все пустые биты справа будут заполнены нулями, независимо от того, подписан ли номер или нет, всегда будут сдвинуты нули.

2) При сдвиге влево E1 >> E2 все пустые биты слева будут равны 0, если число положительное, и 1, если число отрицательное. Помните, что число без знака никогда не бывает отрицательным. Также некоторые реализации могут также заполнить их 0 на некоторых машинах, даже если число отрицательное, поэтому никогда не полагайтесь на это.

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

For example lets take int i = 7;
i<<2
now i = 0000 0000 0000 0000 0000 0000 0000 0111
perform two left shits according to rule 1 would be:

0000 0000 0000 0000 0000 0000 0001 1100

что даст ему значение 28 (E1 * 2E2 = 7 * 2 * 2 = 28), что совпадает с представлением битовой комбинации.

Теперь позвольте мне объяснить вещь "уменьшенного по модулю", хорошо, если вы сдвигаете большое число, тогда биты с левой стороны будут потеряны, "уменьшенный по модулю" компенсирует это, поэтому, если ваше результирующее значение больше максимального значения что тип данных может содержать, биты слева будут потеряны, а затем: результат = (E1 * 2 * E2)% (maximumValue + 1).

Для различных других случаев просто помните о приведенных выше правилах, и все будет хорошо :)

person Jagdeep Sidhu    schedule 24.11.2012
comment
Вы забыли еще одно правило: поскольку в некоторых процессорах есть инструкция «сдвиг на N», которая работает только для значений N меньше, чем размер слова, все ставки отключены [все может случиться], если количество бит для сдвига превышает размер левый операнд. - person supercat; 30.09.2013

Q2: «значение, уменьшенное по модулю X» означает «значение по модулю X» в математике и может быть записано как «значение% X» в C. Эта часть просто объясняет, как работают целочисленные переполнения.

person David Grayson    schedule 24.11.2012