пятница, 12 марта 2010 г.

Арифиметический и логический сдвиг

О сдвигах
В c/c++ существуют операторы сдвига <<(сдвиг влево) и >>(сдвиг вправо). Однако сдвиг может быть арифметическим (с сохранением знака сдвигаемого числа) и логическим (без сохранения знака).
Как выполнить арифметический/логический сдвиг на c/c++?
1) Арифметический сдвиг
вправо влево
0xfffe>>1=0xffff (-2>>1=-1)
выполняется просто
unsigned short shift = 0x1;
short src = 0xfffe;
short out = src >> shift;

0xfffe<<1=0xfffc (-2<<1=-4)>

unsigned short shift = 0x1;
short src = 0xfffe;
short out = src >> shift;

сработает, но скажем для примера 0x8001<<1=0x8002 такой код будет неверен . Следовательно, необходимо сохранять знак

out=(out>0)?((src>>shift)&0x7fff):((src>>shift)|0x8000)

2) Логический сдвиг

вправо влево
0xfffe>>1=0x7fff
unsigned short shift = 0x1;
unsigned short src = 0xfffe;
short out = src >> shift;

0x7ff0<<0x1=0xffe0

unsigned short shift = 0x1;
unsigned short src = 0xfffe;
short out = src <<>

Итак, неочевидные моменты. Если делать сдвиг над беззнаковым типом, то он ведет себя, как логический и не надо ждать от программы, что она будет сохранять знак (умножать/делить на 2 в степени величины сдвига). Если же делать сдвиг над знаковыми типом, то не надо ждать от программы, что она просто подвинет биты в числе.

Что почитать

1) Википедия. Битовый сдвиг

2) МСДН Bitwise shift operator


1 комментарий:

  1. Маленькая неточность... Не знаю, как в С++ (подозреваю, что должно быть так же как и в С), но согласно стандарту С99 (и вряд ли это изменилось в следующих версиях, потому что backward compatibility превыше всего): если вы сдвигаете влево знаковое целое (отрицательное), либо беззнаковое, но такое, что результат не влезает, то вы получаете undefined behavior.

    The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with
    zeros. If E1 has an unsigned type, the value of the result is E1 * 2^E2, reduced modulo
    one more than the maximum value representable in the result type. If E1 has a signed
    type and nonnegative value, and E1 * 2^E2 is representable in the result type, then that is
    the resulting value; otherwise, the behavior is undefined.

    Другой вопрос, что во всех почти компиляторах это undefined behavior реализовано так, как описано у вас.

    ОтветитьУдалить