О сдвигах
В c/c++ существуют операторы сдвига <<(сдвиг влево) и >>(сдвиг вправо). Однако сдвиг может быть арифметическим (с сохранением знака сдвигаемого числа) и логическим (без сохранения знака).
Как выполнить арифметический/логический сдвиг на c/c++?
1) Арифметический сдвиг
В 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; сработает, но скажем для примера 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 src = 0xfffe; short out = src <<> |
Итак, неочевидные моменты. Если делать сдвиг над беззнаковым типом, то он ведет себя, как логический и не надо ждать от программы, что она будет сохранять знак (умножать/делить на 2 в степени величины сдвига). Если же делать сдвиг над знаковыми типом, то не надо ждать от программы, что она просто подвинет биты в числе.
Что почитать
1) Википедия. Битовый сдвиг
2) МСДН Bitwise shift operator
Маленькая неточность... Не знаю, как в С++ (подозреваю, что должно быть так же как и в С), но согласно стандарту С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 реализовано так, как описано у вас.