Поменять два бита с одной операцией в C?

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

???1?0??

и я хочу поменять местами биты 2 и 4 (без изменения ? значение):

???0?1??

но как бы я это сделал в одну операцию в C?

Я выполняю эту операцию тысячи раз в секунду на микроконтроллере, поэтому производительность является главным приоритетом.

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

8 ответов


попробуй:

x ^= 0x14;

это переключает оба бита. Это немного неясно, поскольку вы сначала упоминаете swap, а затем даете пример переключения. Во всяком случае, чтобы поменять биты:

x = precomputed_lookup [x];

где precomputed_lookup массив 256 байт, может быть самым быстрым способом, это зависит от скорости памяти относительно скорости процессора. В противном случае, это:

x = (x & ~0x14) | ((x & 0x10) >> 2) | ((x & 0x04) << 2);

EDIT: дополнительная информация о переключении битов.

когда вы xor (^) двух целых значения вместе, xor выполняется на битовом уровне, как это:

for each (bit in value 1 and value 2)
   result bit = value 1 bit xor value 2 bit

так что бит 0 первого значения xor'ED с битом 0 второго значения, бит 1 с битом 1 и так далее. Операция xor не влияет на другие биты значения. По сути, это параллельный бит XOR на многих бит.

глядя на таблицу истинности для xor, вы увидите, что xor'ING немного со значением " 1 " эффективно переключает бит.

 a  b a^b
 0  0  0
 0  1  1
 1  0  1
 1  1  0

Итак, для переключения бит 1 и 3, напишите двоичное число с тем, где вы хотите, чтобы бит переключался, и нулем, где вы хотите оставить значение неизменным:

00001010

преобразовать в hex: 0x0a. Вы можете переключать столько бит, сколько хотите:

0x39 = 00111001

переключит биты 0, 3, 4 и 5


вы не можете "поменять" два бита (т. е. биты меняют места, а не значение) в одной инструкции, используя бит-fiddling.

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

BYTE lookup[256] = {/* left this to your imagination */};

for (/*all my data values */) 
  newValue = lookup[oldValue];

следующий метод не является ни одной инструкцией C,это просто еще один метод немного возиться. Метод был упрощен от замена отдельных битов с помощью XOR.

как говорится в Родди таблица подстановки будет лучше. Я предлагаю это только на тот случай, если вы не хотите его использовать. Это действительно будет также менять биты, а не просто переключаться (то есть все, что находится в бит 2, будет в 4 и наоборот).

  • b: ваш первоначальное значение ???1?0?? например,
  • x: просто temp
  • r: результат

    x = ((b >> 2) ^ (b > > 4)) & 0x01
    r = b ^ ((x

быстрое объяснение: получите два бита, которые вы хотите посмотреть, и XOR их, сохраните значение x. Смещая это значение обратно в биты 2 и 4 (и OR'ING вместе), вы получаете маску, которая при XORed back с b поменяет местами два исходных бита. В таблице ниже показаны все возможные случаи.

bit2: 0 1 0 1  
bit4: 0 0 1 1  
x   : 0 1 1 0   <-- Low bit of x only in this case 
r2  : 0 0 1 1  
r4  : 0 1 0 1

Я не полностью проверил это, но для нескольких случаев, которые я пробовал быстро, это, казалось, сработало.


это может быть не оптимизировано, но оно должно работать:

unsigned char bit_swap(unsigned char n, unsigned char pos1, unsigned char pos2)
{
    unsigned char mask1 = 0x01 << pos1;
    unsigned char mask2 = 0x01 << pos2;
   if ( !((n & mask1) != (n & mask2)) )
        n ^= (mask1 | mask2);
    return n;
}

функция ниже поменяет местами биты 2 и 4. Вы можете использовать это для предварительного вычисления таблицы подстановки, если это необходимо (так что замена становится одной операцией):

unsigned char swap24(unsigned char bytein) {
    unsigned char mask2 = ( bytein & 0x04 ) << 2;
    unsigned char mask4 = ( bytein & 0x10 ) >> 2;
    unsigned char mask  = mask2 | mask4 ;
    return ( bytein & 0xeb ) | mask;
}

Я написал каждую операцию на отдельной строке, чтобы сделать ее более ясной.


скажем, ваше значение x i.e, x=???1?0??

эти два бита могут быть переключены этой операцией:

x = x ^ ((1<<2) | (1<<4));

#include<stdio.h>

void printb(char x) {
    int i;
    for(i =7;i>=0;i--) 
        printf("%d",(1 & (x >> i)));
    printf("\n");
}

int swapb(char c, int p, int q) {
    if( !((c & (1 << p)) >> p) ^ ((c & (1 << q)) >> q) )
        printf("bits are not same will not be swaped\n");
    else {
        c = c ^ (1 << p);
        c = c ^ (1 << q);
    }
    return c;
}

int main() 
{
    char c = 10;
    printb(c);
    c = swapb(c, 3, 1);
    printb(c);
    return 0;
}

void swap_bits(uint32_t& n, int a, int b) {
    bool r = (n & (1 << a)) != 0;
    bool s = (n & (1 << b)) != 0;

    if(r != s) {
        if(r) {
            n |= (1 << b);
            n &= ~(1 << a);
        }
        else {
            n &= ~(1 << b);
            n |= (1 << a);
        }
    }
}

n - это целое число, которое вы хотите поменять местами,a и b - Это позиции (индексы) битов, которые вы хотите поменять местами, считая от менее значимого бита и начиная с нуля.

используя Ваш пример (n = ???1?0??), вы бы назвали функцию следующим образом:

swap_bits(n, 2, 4);

обоснование: вам нужно только поменять местами биты, если они разные (вот почему r != s). В этом случае один из них равен 1, а другой-0. После этого, просто обратите внимание, что вы хотите сделать ровно один бит set операции и один немного понятно операции.