В C, операторы сдвига (<<
, >>
) арифметика или логичный?
Согласно K& R 2-й выпуск результаты являются зависящими от реализации для сдвигов вправо значений со знаком.
Википедия говорит, что C/C++ 'обычно' реализует арифметический сдвиг на значениях со знаком.
В основном необходимо или протестировать компилятор или не полагаться на него. Моя справка VS2008 для текущего компилятора C++ MS говорит, что их компилятор делает арифметический сдвиг.
При смещении оставленный, нет никакого различия между арифметическим и логическим сдвигом. При смещении права тип сдвига зависит от типа смещаемого значения.
(Как фон для тех читателей, незнакомых с различием, "логический" сдвиг вправо 1 сдвигом разряда все биты направо и заполняет крайний левый бит с 0. "Арифметический" сдвиг оставляет исходное значение в крайнем левом бите. Различие становится важным при контакте с отрицательными числами.)
При смещении неподписанного значения,>> оператор в C является логическим сдвигом. При смещении значения со знаком>> оператор является арифметическим сдвигом.
, Например, принимая машину на 32 бита:
signed int x1 = 5;
assert((x1 >> 1) == 2);
signed int x2 = -5;
assert((x2 >> 1) == -3);
unsigned int x3 = (unsigned int)-5;
assert((x3 >> 1) == 0x7FFFFFFD);
С точки зрения типа сдвига Вы добираетесь, важной вещью является тип значения, которое Вы смещаете. Классический источник ошибок - при смещении литерала к, скажем, нулевым разрядам маски. Например, если бы Вы хотели отбросить крайний левый бит целого числа без знака, тогда Вы могли бы попробовать это как свою маску:
~0 >> 1
, К сожалению, это получит Вас в проблему, потому что маска будет иметь весь свой набор битов, потому что значение, смещаемое (~0), подписывается, таким образом арифметический сдвиг выполняется. Вместо этого Вы хотели бы вызвать логический сдвиг путем явного объявления значения как неподписанного, т.е. путем выполнения чего-то вроде этого:
~0U >> 1;
Ну, я смотрел это на Википедию , и они говорят следующее:
C, однако, имеет только один оператор сдвига вправо,>>. Много компиляторов C выбирают, какой сдвиг вправо выполнить в зависимости от того, какое целое число смещается; часто целые числа со знаком смещаются с помощью арифметического сдвига, и целые числа без знака смещаются с помощью логического сдвига.
, Таким образом, это походит, это зависит от Вашего компилятора. Также в той статье, обратите внимание, что сдвиг влево является тем же для арифметики и логичный. Я рекомендовал бы делать простой тест с некоторыми и неподписанными числами со знаком на случае границы (высокий набор битов, конечно) и видел бы то, что результат находится на Вашем компиляторе. Я также рекомендовал бы избежать в зависимости от него являющийся один или другой, так как кажется, что C не имеет никакого стандарта, по крайней мере, если это разумно и возможно избежать такой зависимости.
gcc будет обычно использовать логические сдвиги на неподписанных переменных и для сдвигов влево на переменных со знаком. Арифметический сдвиг вправо является действительно важным, потому что это подпишется, расширяют переменную.
желание gcc будет использовать это, когда применимо, как другие компиляторы, вероятно, сделают.
Вот функции, гарантирующие логический сдвиг вправо и арифметический сдвиг вправо для int в C:
int logicalRightShift(int x, int n) {
return (unsigned)x >> n;
}
int arithmeticRightShift(int x, int n) {
if (x < 0 && n > 0)
return x >> n | ~(~0U >> n);
else
return x >> n;
}