Ответ 1
int lsr(int x, int n)
{
return (int)((unsigned int)x >> n);
}
Я работаю над созданием логической функции сдвига справа в C, использующей только побитовые операторы. Вот что у меня есть:
int logical_right_shift(int x, int n)
{
int size = sizeof(int); // size of int
// arithmetic shifts to create logical shift, return 1 for true
return (x >> n) & ~(((x >> (size << 3) - 1) << (size << 3) -1)) >> (n-1);
}
Это действительно работает для всех случаев, кроме случаев, когда n = 0. Я пытался выяснить способ исправить это, так что он будет работать и для n = 0, но я застрял.
int lsr(int x, int n)
{
return (int)((unsigned int)x >> n);
}
Это то, что вам нужно:
int logical_right_shift(int x, int n)
{
int size = sizeof(int) * 8; // usually sizeof(int) is 4 bytes (32 bits)
return (x >> n) & ~(((0x1 << size) >> n) << 1);
}
Поясните
x >> n
сдвигает n bits
вправо. Однако, если x
отрицательный, бит знака (самый старший бит) будет скопирован справа, например:
Предположим, что каждый int 32 бита, пусть
x = -2147483648 (10000000 00000000 00000000 00000000)
, затем
x >> 1 = -1073741824 (11000000 00000000 00000000 00000000)
x >> 2 = -536870912 (11100000 00000000 00000000 00000000)
и т.д.
Итак, нам нужно стереть эти знаковые дополнительные знаковые биты, когда n отрицательно.
Предположим n = 5
здесь:
0x1 << size
перемещает 1
в крайнее левое положение:
(10000000 00000000 00000000 00000000)
((0x1 << size) >> n) << 1
копирует 1 в его n-1
соседи:
(11111000 00000000 00000000 00000000)
~((0x1 << size) >> n) << 1!
отменяет все биты:
(00000111 11111111 11111111 11111111)
чтобы мы, наконец, получили маску, чтобы извлечь то, что действительно нужно от x >> n
:
(x >> n) & ~(((0x1 << size) >> n) << 1)
Операция &
выполняет трюк.
И общая стоимость этой функции - 6
.
Просто сохраните int
в unsigned int
и выполните >>
на нем.
(Знак не расширен или не сохраняется, если вы используете unsigned int)
Я думаю, что проблема в вашей части " → (n-1)". Если n равно 0, то левая часть будет сдвигаться на -1. Итак, вот мое решение
int logical_right_shift(int x, int n)
{
int mask = ~(-1 << n) << (32 - n);
return ~mask & ( (x >> n) | mask);
}
Получено из PHP-реализация логического смещения вправо
function logical_right_shift( i , shift ) {
if( i & 2147483648 ) {
return ( i >> shift ) ^ ( 2147483648 >> ( shift - 1 ) );
}
return i >> shift;
}
Только для 32-разрядных платформ.
int logicalShift(int x, int n) {
int mask = x>>31<<31>>(n)<<1;
return mask^(x>>n);
}
Только для 32 бит
Как и с комментарием @Ignacio, я не знаю, почему вы хотели бы это сделать (просто не делайте приведение к unsigned
, как в других ответах), но как насчет (предполагая два дополнения и двоичные файлы, и что подписанные сдвиги являются арифметическими):
(x >> n) + ((1 << (sizeof(int) * CHAR_BIT - n - 1)) << 1)
или
(x >> n) ^ ((INT_MIN >> n) << 1)
Ответ на Milnex велик и имеет удивительное объяснение, но реализация, к сожалению, терпит неудачу из-за сдвига на общий размер. Вот рабочая версия:
int logicalShift(int x, int n) {
int totalBitsMinusOne = (sizeof(int) * 8) - 1; // usually sizeof(int) is 4 bytes (32 bits)
return (x >> n) & ~(((0x1 << totalBitsMinusOne) >> n) << 1);
}
Чтобы иметь 1 как самый старший бит и все нули в другом месте, нам нужно сдвинуть 0x1
на number of bits - 1
. Я отправляю свой собственный ответ, потому что мое редактирование принятого ответа было каким-то образом отвергнуто.