12

假设我有一个包含六个未知值的字节:

???1?0??

我想交换第 2 位和第 4 位(更改任何?值):

???0?1??

但是我将如何在 C 中的一个操作中做到这一点?

我在微控制器上每秒执行数千次此操作,因此性能是重中之重。

“切换”这些位会很好。尽管这与交换位不同,但切换对于我的目的来说可以正常工作。

4

8 回答 8

30

尝试:

x ^= 0x14;

这会切换两个位。当您首先提到交换然后给出一个切换示例时,这个问题有点不清楚。无论如何,交换位:

x = precomputed_lookup [x];

其中 precomputed_lookup 是一个 256 字节的数组,可能是最快的方式,它取决于相对于处理器速度的内存速度。否则,它是:

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

编辑:有关切换位的更多信息。

当您^对两个整数值进行异或 ( ) 时,会在位级别执行异或,如下所示:

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

这样第一个值的位 0 与第二个值的位 0 异或,位 1 与位 1 异或,依此类推。异或运算不会影响值中的其他位。实际上,它是许多位的并行位异或。

查看 xor 的真值表,您会看到值 '1' 的 xor'ing 位有效地切换了该位。

 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

于 2009-06-11T14:59:27.943 回答
11

您不能使用位摆弄在单个指令中“交换”两个位(即位改变位置,而不是值)。

如果您想真正交换它们,最佳方法可能是查找表。这适用于许多“尴尬”的转换。

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

for (/*all my data values */) 
  newValue = lookup[oldValue];
于 2009-06-11T15:07:28.703 回答
5

以下方法不是单个 C 指令,它只是另一种摆弄方法。该方法通过使用 XOR 交换单个位进行了简化。

正如罗迪的回答中所述,最好使用查找表。我只建议您在不想使用的情况下这样做。这确实也会交换位,而不仅仅是切换(也就是说,位 2 中的任何内容都将位于 4 中,反之亦然)。

  • b: 你的原始值 - ???1?0?? 例如
  • x:只是一个温度
  • r:结果

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

快速解释:获取您想要查看的两位并对它们进行异或,将值存储到x. 通过将此值移回第 2 位和第 4 位(以及 OR'ing 一起),您将获得一个掩码,当与它进行 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

我没有对此进行全面测试,但对于我快速尝试的少数情况,它似乎有效。

于 2009-06-11T16:24:32.403 回答
4

这可能没有被优化,但它应该可以工作:

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;
}
于 2009-11-04T06:07:35.990 回答
2

下面的函数将交换位 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;
}

我将每个操作写在单独的行上以使其更清晰。

于 2009-06-11T16:39:04.740 回答
0

假设您的值为 x 即 x=???1?0??

通过此操作可以切换这两个位:

x = x ^ ((1<<2) | (1<<4));
于 2010-01-15T11:39:58.467 回答
0
#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;
}
于 2013-11-13T10:29:57.443 回答
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。之后,请注意您要执行一位设置操作和一位清除操作。

于 2015-10-06T08:08:18.873 回答