Bit Twiddling - путается с выходом этой программы - PullRequest
1 голос
/ 29 марта 2012

Так что я возился с Bit-Twiddling в C, и я наткнулся на интересный вывод:

int main()
{
    int a = 0x00FF00FF;
    int b = 0xFFFF0000;

    int res = (~b & a);

    printf("%.8X\n", (res << 8) | (b >> 24));
}

И вывод этого утверждения:

FFFFFFFF

Я ожидал, что результат будет

0000FFFF

Но почему не так? Я что-то упускаю из-за сдвига битов здесь?

Ответы [ 4 ]

5 голосов
/ 29 марта 2012

TLDR: ваше целое число b отрицательно, поэтому при смещении вправо значение самого старшего бита (т. Е. 1) остается прежним.Поэтому, когда вы сдвигаете b вправо на 24 места, вы в конечном итоге получаете 0xFFFFFFFF.

Более подробное объяснение:

Предполагая на вашей платформе, что ваши целые числа 32-битные или более длинные, а целое число со знаком представляет 2дополнить затем 0xFFFF0000, назначенный целой переменной со знаком, является отрицательным числом.Если int длиннее 32 бит, то 0xFFFF0000 будет сначала расширяться знаком и будет по-прежнему отрицательным числом.

Сдвиг вправо на отрицательное число является реализацией, определенной стандартом (C99 / N1256, раздел 6.5.7.5):

Результатом E1 >> E2 является E1-сдвинутая вправо позиция бита E2.[...] Если E1 имеет тип со знаком и отрицательное значение, полученное значение определяется реализацией.

Это означает, что конкретный компилятор может выбирать, что происходит в конкретной ситуации, но это должно бытьв руководстве по компилятору указано, каков эффект.

Во многих процессорах, как правило, существует два набора команд сдвига: логический сдвиг и арифметический сдвиг.Логическое смещение вправо сместит биты и заполнит выставленные биты нулями.Арифметические сдвиги вправо (если допустить повторение дополнения до 2) будут заполнять открытые биты тем же самым битовым значением старшего значащего бита, чтобы он заканчивался результатом, который согласуется с использованием сдвигов в качестве деления на 2. (Например, -4>> 1 == 0xFFFFFFFC >> 1 == 0xFFFFFFFE == -2.)

В вашем случае оказывается, что разработчик компилятора выбрал использование арифметических сдвигов при применении к целым числам со знаком и, следовательно, результат сдвигаотрицательное значение справа остается отрицательным значением.В терминах битовых шаблонов 0xFFFF0000 >> 24 дает 0xFFFFFFFF.

Если вы не абсолютно уверены в том, что вы делаете, лучше всего выполнять побитовые операции только с неподписанными типами, поскольку их внутреннее представление можно безопасно рассматривать как коллекциюбит.Возможно, вы также захотите убедиться, что все числовые значения, которые вы используете в этом случае, не подписаны, добавив суффикс без знака к вашему номеру.

2 голосов
/ 29 марта 2012

Смещающие вправо отрицательные значения (например, b) могут быть определены двумя различными способами: логический сдвиг , который дополняет значение нулями слева (что дает положительное число при смещении ненулевого значения)сумму) и арифметический сдвиг , который дополняет значение единицами (всегда получая отрицательное число).То, какое определение используется в C, определяется реализацией, и ваш компилятор, очевидно, использует арифметическое смещение, поэтому b >> 24 равно 0xFFFFFFFF.

1 голос
/ 29 марта 2012

b >> 24 дает 0xFFFFFFFF правый блокнот со знаком с отрицательным числом

List = (res << 8) | (b >> 24)
a        = 0x00FF00FF =  0000 0000 1111 1111 0000 0000 1111 1111
b        = 0xFFFF0000 =  1111 1111 1111 1111 0000 0000 0000 0000
~b       = 0x0000FFFF =  0000 0000 0000 0000 1111 1111 1111 1111
~b & a   = 0x000000FF =  0000 0000 0000 0000 0000 0000 1111 1111, = res
res << 8 = 0x0000FF00 =  0000 0000 0000 0000 1111 1111 0000 0000
 b >> 24 = 0xFFFFFFFF =  1111 1111 1111 1111 1111 1111 1111 1111
List     = 0xFFFFFFFF =  1111 1111 1111 1111 1111 1111 1111 1111
0 голосов
/ 29 марта 2012

Золотое правило: никогда не смешивайте числа со знаком с побитовыми операторами.

Измените все целые на беззнаковые. В качестве меры предосторожности измените также все литералы на unsigned.

#include <stdint.h>

uint32_t a = 0x00FF00FFu;
uint32_t b = 0xFFFF0000u;

uint32_t res = (~b & a);
...