Почему при сдвиге -1 на 1 бит мы получаем -1
Почему при сдвиге -1 на 1 бит мы получаем -1, если первый бит, который отвечает за знак сдвигается и по идее должно быть положительное число
std::cout << (-1 >> 1);
Ответы (4 шт):
В архитектурах x86/amd64 существует два вида сдвига вправо: бесзнаковый и знаковый. В первом случае старшие биты после сдвига заполняются нулями. Во втором - знаковыми разрядами, т.е. нулями для положительных чисел и единицами для отрицательных (у всех отрицательных знаковых целых старший бит равен 1).
Какой вид сдвига применяется для оператора >> в C++? Тут всё очень просто: для бесзнаковых целых (unsigned int) - это бесзнаковый сдвиг, для целых со знаком (int), соответственно, - знаковый.
Из-за особенности машинного представления отрицательных целых в x86/amd64, у числа -1 все биты равны единице. Следовательно, после знакового сдвига вправо старшие биты устанавливаются в единицу, и как результат, все биты получившегося числа остаются равными единице, т.е. получается всё та же -1.
P.S. Рекомендую почитать про представление целых чисел в т.н. дополнительном коде.
P.P.S. Ещё один занимательный факт: если у числа -2147483648 (тип int) поменять знак, то получится то же самое число -2147483648.
Биты отрицательных чисел перевернуты, поэтому при сдвиге вправо 0 становятся 1. А знаковый бит который 1 так и так не будет менятся. В случае с -1 все биты 1 так что тут кроме -1 при сдвиге вправо -1 на 1 ничего другого и быть не может.
Но результат, вобще говоря от реализации зависит. Хотя стоит отметить, что в основном все реализации делают именно такой сдвиг.
... ибо сказано в стандарте c++20 :) —
The value of
E1 >> E2isE1/2^E2, rounded down. [Note:E1is right-shiftedE2bit positions. Right-shift on signed integral types is an arithmetic right shift, which performs sign-extension. —end note]
Впрочем, еще в стандарте c++17 было сказано иное:
The value of
E1 >> E2isE1right-shiftedE2bit positions. IfE1has an unsigned type or ifE1has a signed type and a non-negative value, the value of the result is the integral part of the quotient ofE1/2^E2. IfE1has a signed type and a negative value, the resulting value is implementation-defined.
Но в целом стандарт C++20 просто узаконил сложившуюся практику.
В дополнительном коде число -1 хранится как число со всеми установленными битами.
Для байта это 11111111b, для слова 1111111111111111b, для 4-байтного целого 11111111111111111111111111111111b и так далее.
Для одного байта: если мы сдвинем 11111111b вправо на один бит мы получим x1111111b, и старший бит нужно заполнить или 0 или 1. Если мы подставит 0, то получим 01111111b, что равно 127, то есть максимально положительно знаковое значение данной длины. Если подставим 1 - получим опять 11111111b, то есть -1.
Для двух байтов соответственно мы получим или -1 или 32767. Для четырёх - -1 или 2147483647.
Авторы языка решили, что получить -1 будет более логично.