Например:
unsigned int numA = 66; // or anything really
unsigned int numB = -numA;
unsigned int numC = numA & numB
Я понимаю, что побитовый оператор дополнения может быть использован для получения дополнения до двух (в сочетании с +1).
Причина, по которой я спрашиваю, состоит в том, что я наткнулся на это в каком-то коде для шахматного движка. Шахматные движки делают много «хакерских» вещей, чтобы получить абсолютную скорость, особенно в функциях генерации ходов, которые вызываются миллионы раз в секунду. (Не помогает, что это был пример генерации магического битборда — самый оптимизированный из всех). В частности, этот код шахматного движка корректно работает только при компиляции gcc (я подозреваю).
Как разные компиляторы относятся к этому? В частности, как gcc справляется с этим по сравнению с компилятором C ++ в VS Studio 2012 Express.
Благодарю.
Соответствующая цитата из Стандарта фактически такова:
(§5.3.1 / 8) Операнд унарного оператора должен иметь арифметический или неисчислимый тип перечисления, и результатом является отрицание его операнда. Интегральное продвижение выполняется для целочисленных или перечислимых операндов. Отрицательное число без знака вычисляется путем вычитания его значения из 2N, где n — количество битов в повышенном операнде. Тип результата — это тип повышенного операнда.
(Это из C ++ 11; раньше оно было 5.3.1 / 7 в более старых версиях.)
Так -num
будет оцениваться как 2CHAR_BIT * SizeOf (NUM) — Num (‡). Результат будет того же типа, что и операнд (после целочисленного преобразования), то есть он также будет без знака.
Я только что проверил с GCC, и, кажется, выполняет операцию точно так, как описано в стандарте. Я предполагаю, что это относится и к Visual C ++; в противном случае это ошибка.
(‡) Эта формула предполагает, что соответствующие количество бит соответствует размеру (в битах) переменной в памяти. Как указывает Кит Томпсон в комментарии, это не может быть правдой, если есть биты заполнения (то есть, когда не все биты участвуют в представлении числового значения, что возможно в соответствии с §3.9.1 / 1). В системе, которая использует больше битов для хранения значения, чем используется для представления числового значения, формула не будет точной. (Лично я, на самом деле, не знаю ни о какой такой системе.)
Вот что говорит стандарт C ++ в разделе 4.7.2 (Интегральные преобразования):
Если тип назначения не подписан, полученное значение является наименьшим
целое число без знака, совпадающее с целым числом источника (по модулю 2N где п
количество битов, используемых для представления типа без знака). [Примечание: в
это дополняющее представление двух, это преобразование является концептуальным и
в битовой комбинации нет изменений (если нет усечения).
—Конечная записка]
Надеюсь, что это ответ на ваш вопрос.
Вы спрашивали о C и C ++. Помните, что это два разных языка. В этом конкретном случае у них одинаковые правила для операций с неподписанными типами, но они выражаются по-разному.
Цитируя последний проект действующего (2011 г.) стандарта ISO C), раздел 6.2.5p9:
Вычисления с участием беззнаковых операндов никогда не могут переполниться, потому что
результат, который не может быть представлен результирующим целым числом без знака
тип уменьшается по модулю число, которое на единицу больше наибольшего
значение, которое может быть представлено результирующим типом.
Описание унарного оператора «-» просто говорит о том, что результатом является «отрицание его (повышенного) операнда»; Предполагается, что читатель прочитал 6.2.5, чтобы выяснить, что такое «минус» целого числа без знака.
На любом языке результат:
unsigned int numA = 66;
unsigned int numB = -numA;
это хранить UINT_MAX - 66U + 1U
в numB
, (The U
суффиксы на самом деле не нужны, но я включаю их, чтобы подчеркнуть, что все это определяется в терминах беззнаковых значений.)
Я был укушен typeof (-Unsigned) является Unsigned. Компилятор VS2012 выводит это на интересные уровни непостижимого поведения:
без знака x = 0xFFFFFFFE;
int y = -x / 2;
Что такое «у»?
Я ожидал бы x / 2 = 0x7FFFFFFF, тогда — (x / 2) = 0x80000001, то есть -2 ** 31-1.
Вместо этого компилятор генерирует (-x) = 0x00000002, (-x) / 2 = 0x00000001.
Я думаю, что для граничных значений, это все Deathstar 9000. Вздох.