Поменять два бита одной операцией в C? - PullRequest
12 голосов
/ 11 июня 2009

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

???1?0??

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

???0?1??

Но как мне сделать это за одну операцию в C?

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

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

Ответы [ 8 ]

30 голосов
/ 11 июня 2009

Попробуйте:

x ^= 0x14;

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

x = precomputed_lookup [x];

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

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

РЕДАКТИРОВАТЬ: Еще немного информации о переключении битов.

Когда вы 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 со значением '1' эффективно переключает бит.

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

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

00001010

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

0x39 = 00111001

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

11 голосов
/ 11 июня 2009

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

Оптимальный подход, если вы действительно хотите поменять их местами, - это, вероятно, справочная таблица. Это справедливо для многих «неловких» преобразований.

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

for (/*all my data values */) 
  newValue = lookup[oldValue];
5 голосов
/ 11 июня 2009

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

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

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

    x = ((b >> 2) ^ (b >> 4)) & 0x01
    r = b ^ ((x << 2) | (x << 4)) </p>

Краткое объяснение: получите два бита, которые вы хотите посмотреть, и XOR их, сохраните значение в x. Сдвигая это значение обратно к битам 2 и 4 (и ИЛИ 'вместе), вы получаете маску, которая при возвращении XOR с 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

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

4 голосов
/ 04 ноября 2009

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

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 голосов
/ 11 июня 2009

Приведенная ниже функция поменяет местами биты 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;
}

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

0 голосов
/ 06 октября 2015
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. После этого просто обратите внимание, что вы хотите выполнить ровно одну битовую установку и одну битовую очистку операцию.

0 голосов
/ 13 ноября 2013
#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;
}
0 голосов
/ 15 января 2010

Скажите, что ваше значение равно x, т.е. x = ??? 1? 0 ??

Эта операция может переключать два бита:

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