Арифметическое смещение битов на целых числах со знаком - PullRequest
48 голосов
/ 24 октября 2010

Я пытаюсь выяснить, как именно арифметические операторы сдвига битов работают в C, и как это повлияет на 32-разрядные целые числа со знаком.

Чтобы упростить задачу, скажем, мы работаем в пределах одного байта (8 бит):

x = 1101.0101
MSB[ 1101.0101 ]LSB

Читая другие посты на Stack Overflow и на некоторых веб-сайтах, я обнаружил, что: << сместится в сторону MSB (в моем случае влево) и заполнит «пустой» LSBбиты с 0 с.

и >> сместятся в сторону LSB (в моем случае вправо) и заполнят "пустые" биты битом MS

Так, x = x << 7 приведет кпереводя LSB в MSB и устанавливая все в 0 с.

1000.0000

Теперь, скажем, я бы >> 7, последний результат.Это приведет к [0000.0010]?Я прав?

Прав ли я в своих предположениях об операторах смены?

Я только что проверил на своей машине, **

int x = 1;   //000000000......01

x = x << 31; //100000000......00

x = x >> 31; //111111111......11 (Everything is filled with 1s !!!!!) 

Почему?

Ответы [ 5 ]

56 голосов
/ 24 октября 2010

Сдвиг вправо отрицательного числа со знаком имеет поведение, определяемое реализацией.

Если ваши 8 бит предназначены для представления 8-битного значения со знаком (как вы уже говорили о «32-битном целом числе со знаком» ранее)переход на 8-битные примеры) тогда у вас есть отрицательное число.Сдвиг вправо может заполнить «пустые» биты исходным MSB (т. Е. Выполнить расширение знака) или он может сместиться в нули, в зависимости от платформы и / или компилятора.

(поведение, определяемое реализацией, означает, что компиляторсделайте что-нибудь разумное, но зависящее от платформы, в документации компилятора должно быть указано, что.)


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

(неопределенное поведение означает, что может произойти все что угодно.)


Те же самые операции с значениями без знака четко определены в обоих случаях: «пустые» биты будут заполнены 0.

35 голосов
/ 24 октября 2010

Операции побитового сдвига не определены для отрицательных значений

для '<<' </p>

6.5.7 / 4 [...] Если E1 имеет тип со знаком и неотрицательныйзначение, и E1 × 2 E2 представимо в типе результата, то есть итоговое значение;в противном случае поведение не определено.

и для '>>'

6.5.7 / 5 [...] Если E1 имеет тип со знаком и минусзначение, результирующее значение определяется реализацией.

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

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

При рассмотрении только неотрицательных значений эффект сдвига влево1 (expression << 1) аналогично умножению выражения на 2 (при условии, что выражение * 2 не переполняется), а эффект смещения вправо на 1 (expression >> 1) аналогичен делению на 2.

5 голосов
/ 25 октября 2010

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

Большинство реализаций обрабатывают сдвиг вправо со знаком как пол (x / 2 N ), заполняя сдвинутые биты с помощью знакового бита. Это очень удобно на практике, так как эта операция очень распространена. С другой стороны, если вы сдвинете целое число без знака вправо, сдвинутые в битах будут обнулены.

Если смотреть со стороны машины, большинство реализаций имеют два типа команд вправо-влево:

  1. «арифметический» сдвиг вправо (часто с мнемоническим ASR или SRA), который работает, как я объяснил.

  2. «Логический» сдвиг вправо (часто с мнемоническим LSR или SRL или SR), который работает так, как вы ожидаете.

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

1 голос
/ 07 мая 2019

Начиная с c ++ 20 операторы побитового сдвига для целых чисел со знаком хорошо определены.

Сдвиг влево a<<b эквивалентно a*2^b modulus 2^N, где N - количество битов в результирующем типе.В частности, 1<<31 на самом деле является наименьшим значением int.

Сдвиг вправо a>>b эквивалентен a/2^b, округленному в меньшую сторону (т.е. к отрицательной бесконечности).Например, -1>>10 == -1.

Для получения более подробной информации см. https://en.cppreference.com/w/cpp/language/operator_arithmetic.

(более старые стандарты см. В ответе Мэтью Слэттери)

0 голосов
/ 05 июля 2017

В 32-битном компиляторе

x = x >> 31;

здесь x - целое число со знаком, поэтому 32-й бит - знаковый бит.

конечное значение x равно 100000 ... 000 .и 32-й бит указывают -ive значение.

здесь значение x соответствует комплименту 1.

тогда конечный x равен -32768

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...