假设我有一个包含六个未知值的字节:
???1?0??
我想交换第 2 位和第 4 位(不更改任何?
值):
???0?1??
但是我将如何在 C 中的一个操作中做到这一点?
我在微控制器上每秒执行数千次此操作,因此性能是重中之重。
“切换”这些位会很好。尽管这与交换位不同,但切换对于我的目的来说可以正常工作。
假设我有一个包含六个未知值的字节:
???1?0??
我想交换第 2 位和第 4 位(不更改任何?
值):
???0?1??
但是我将如何在 C 中的一个操作中做到这一点?
我在微控制器上每秒执行数千次此操作,因此性能是重中之重。
“切换”这些位会很好。尽管这与交换位不同,但切换对于我的目的来说可以正常工作。
尝试:
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
您不能使用位摆弄在单个指令中“交换”两个位(即位改变位置,而不是值)。
如果您想真正交换它们,最佳方法可能是查找表。这适用于许多“尴尬”的转换。
BYTE lookup[256] = {/* left this to your imagination */};
for (/*all my data values */)
newValue = lookup[oldValue];
以下方法不是单个 C 指令,它只是另一种摆弄方法。该方法通过使用 XOR 交换单个位进行了简化。
正如罗迪的回答中所述,最好使用查找表。我只建议您在不想使用的情况下这样做。这确实也会交换位,而不仅仅是切换(也就是说,位 2 中的任何内容都将位于 4 中,反之亦然)。
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
我没有对此进行全面测试,但对于我快速尝试的少数情况,它似乎有效。
这可能没有被优化,但它应该可以工作:
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 即 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。之后,请注意您要执行一位设置操作和一位清除操作。