Арифметический сдвиг вправо примеры в моем учебнике сдвиг в единицах, когда MSB был равен нулю


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

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

Теперь, видя, как арифметический сдвиг вправо заполняет значение самого значительного бита, мне кажется, что это должно быть правильно засрано, как Итак:

00001000
Однако в книге говорится, что это должно быть
11101000

То есть, 1 заполняется слева вместо 0. но разве самый значительный бит не был 0?

Ну, есть и еще один:

0110 0100 >> 3 = 0000 1100

Но, по-видимому, это тоже неправильно, и это должно быть:

11101100

Опять же, я не понимаю, что самое важное значение бита-это явно 0, тот, который дальше всего слева, но решение говорит мне, что 1 должен быть заполнен?


Итак, у меня есть последний здесь, который, по-видимому, является правильным:

0111 0010 >> 3 = 0000 1110

Это то, что я ожидал. Так почему же эти другие не правы?

Чтение сборки невероятно трудно без понимания этого, так как много умножения и деления компилируется для операций сдвига.

3 2

3 ответа:

Ни один сдвиг никогда не заполнится 1, когда MSB входного сигнала был равен 0.Обратите внимание, что обратное не верно (логический сдвиг вправо всегда заполняется нулем).

Если в книге нет какого-то дополнительного контекста, то это просто ошибка. Это не поворот или что-то еще. Даже Дополнение 1 или знак/величина не могут объяснить его (потому что число положительно во всех 3 этих представлениях).

Арифметические сдвиги вправо в сдвиг дополнения 2 в копиях бита знака. (так например, sar eax, 31 передает знаковый бит всем битам 32-разрядного регистра.

Знак результата арифметического сдвига будет всегда таким же, как и знак входного сигнала.


Логические сдвиги вправо всегда смещаются в нули.
Левые сдвиги одинаковы для логики и арифметики: они сдвигаются в нули. (x86 имеетSal мнемонический , но это просто альтернативное имя для того же кода операции, что и shl. Более поздние расширения x86 не утруждают себя упоминанием арифметический сдвиг влево, например, есть SIMD pslld (упакованный-целочисленный сдвиг влево логический), но нет pslad.)

В знаке / величине арифметические сдвиги влево, я думаю, оставят знак немного нетронутым. Я не уверен, что дополняющие арифметические левые сдвиги 1 нуждаются в каком-либо особом лечении.

Но дополнение 2 арифметический сдвиг влево идентичен логическому. (И идентично add same,same левому сдвигу на 1 ака умножить на 2.)

Сдвиг влево-это увеличение базовой мощности, а вправо-уменьшение. Итак, основание 2 (двоичное) сдвиг вправо-это уменьшение в степени 2, а влево-увеличение. Так что право делится на 2 A слева умножить на 2.

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

Отрицательное число дополнения двойки означает, что msbit установлен как вы четко понимать.

Поэтому, если я хочу разделить -2 0b11111110 на 2, чтобы получить -1 0b11111111, было бы неплохо просто сдвинуть вправо, но логический сдвиг вправо обычно означает заполнение вершины нулями, поэтому 0b11111110 логический сдвиг вправо становится 0b01111111, что не равно -1, это 127. Некоторые процессоры имеют арифметический сдвиг вправо, который вместо сдвига в нули дублирует верхний бит, так что 0b11111110 ASR становится 0b11111111. Какой правильный / желаемый ответ для использования сдвига для разделения -2 / 2.

Также следует понимать, что 0b11111110 в то же время также представляет собой значение 254 и 254/2 = 127. Таким образом, арифметический сдвиг вправо дает 255, что является неправильным ответом. Пока что для подписанного мы хотим сделать ASR для неподписанного LSR.

Вы правы, ваша книга, вероятно, ошибочна, или вы даете нам только фрагменты книги.
0b01000100 ASR is 0b00100010
0b01000100 LSR is 0b00100010
0b10001000 ASR is 0b11000100
0b10001000 LSR 0s 0b01000100

Многобитные арифметические сдвиги просто думайте о них как о множественных единичных битах или просто заполните верхнюю часть msbit оригинала число

0b10001000 ASR 3 = 0bBBB10001, где B - 1 so 0b11110001 для LSR BB-это нули 0b00010001

Обратите внимание, что арифметический сдвиг влево и логический сдвиг влево-это одна и та же операция, они просто заполняют нули справа.

Говоря о C, это было бы ошибкой в книге, согласно стандарту :

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

Итак, сдвиг на беззнаковые числа хорошо определен, но не для знаковых чисел (лучше сказать, нет общего определения).

Для полноты картины стандарт гласит:

Результат E1 > > E2-это E1-сдвинутые вправо позиции битов E2. Если E1 имеет беззнаковый тип или если E1 имеет знаковый тип и неотрицательное значение, то значение результата является неотъемлемой частью коэффициента E1 / 2E2. Если E1 имеет знаковый тип и отрицательное значение, то результирующее значение определяется реализацией ().

Итак принимая во внимание пример ОП (положительные числа), нет никаких оснований заполнять его своими.

Говоря об арифметическом сдвиге (давайте использовать x86 в качестве примера):

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

Инструкция SAR заполнится единицей, когда число отрицательно (чтобы сохранить знак), например:

sar 10110011b, 2 #the result is 11101100b

(shr 10110011b, 2 #the result is 00101100b)

Но опять же, пример OP говорит о положительных числах, поэтому нет причин заполнять их своими. Подытоживая, можно заполнить свои, когда применяется сдвиг вправо, но не в тех случаях.