Неожиданный результат операций побитового сдвига C/C++

Думаю, я схожу с ума от этого.

У меня есть фрагмент кода, который должен создать (беззнаковое) целое число с Nпоследовательными битами, установленными в 1. Точнее, у меня есть битовая маска, и в некоторых ситуациях я хотел бы установить это к твердому rnage.

У меня есть следующая функция:

void MaskAddRange(UINT& mask, UINT first, UINT count)
{
    mask |= ((1 << count) - 1) << first;
}

Простыми словами: 1 << countв двоичном представлении равно 100...000(количество нулей равно count ), вычитание 1 из такого числа дает 011...111, а затем мы просто сдвигаем его влево на first.

Вышеизложенное должно дать правильный результат, когда соблюдается следующее очевидное ограничение:

first + count <= sizeof(UINT)*8 = 32

Обратите внимание, что следуеттакже корректно работают для "крайних" случаев.

  • если count = 0мы имеем (1 << count) = 1, и, следовательно, ((1 << count) - 1) = 0.
  • если count = 32мы имеем (1 << count) = 0, так как старший бит переполняется, и согласно правилам C/C++ операторы побитового сдвига не циклический. Тогда ((1 << count) - 1) = -1 (все биты установлены).

Однако, как оказалось, для count = 32формула не работает должным образом. Как выяснилось:

UINT n = 32;
UINT x = 1 << n;
// the value of x is 1

Кроме того, я использую MSVC2005 IDE. Когда я оцениваю приведенное выше выражение в отладчике, результат равен 0. Однако, когда я перешагиваю вышеприведенную строку, xполучает значение 1. Блокируя через дизассемблер, мы видим следующее:

mov eax,1 
mov ecx,dword ptr [ebp-0Ch] // ecx = n
shl eax,cl                  // eax <<= LOBYTE(ecx)
mov dword ptr [ebp-18h],eax // n = ecx

Там нет действительно магия, компилятор только что использовал инструкцию shl. Тогда кажется, что shlне делает того, что я ожидал. Либо CPU решает проигнорировать эту инструкцию, либо сдвиг обрабатывается по модулю 32, либо не знаю что.

Мои вопросы:

  • Каково правильное поведение инструкций shl/ shr?
  • Существует ли флаг ЦП, управляющий инструкциями побитового сдвига?
  • Это соответствует стандарту C/C++?

Заранее спасибо

Редактировать:

Спасибо за ответы.Я понял, что (1) shl/shrдействительно обрабатывают операнд по модулю 32 (или & 0x1F) и (2) стандарт C/C++ рассматривает сдвиг более чем на 31 бит как неопределенный поведение.

Тогда у меня есть еще один вопрос. Как я могу переписать свое «маскирующее» выражение, чтобы охватить и этот крайний случай. Он должен быть без ветвления ( if, ?). Какое выражение было бы самым простым?

6
задан valdo 25 March 2012 в 14:04
поделиться