我可以在不设置先前值的情况下设置位序列吗?

时间:2011-11-15 18:57:43

标签: c++ c bit-manipulation

我有一系列的比特,比如说

0110 [1011] 1111

假设我想将myddle nybble设置为0111作为新值。

使用ANDOR的位置屏蔽方法,我似乎别无选择,只能先将原始值设置为0000,因为如果我尝试ANDOR反对1011的原始值,我不会得出0111的预期结果。

我应该使用另一个逻辑运算符来获得所需的效果吗?或者我每次都被锁定为2次操作?


善意协助后的结果是:

inline void foo(Clazz* parent, const Uint8& material, const bool& x, const bool& y, const bool& z)
{
    Uint8 offset = x | (y << 1) | (z << 2); //(0-7)
    Uint64 positionMask = 255 << offset * 8; //255 = length of each entry (8 bits), 8 = number of bits per material entry
    Uint64 value = material << offset * 8;
    parent->childType &= ~positionMask; //flip bits to clear given range.
    parent->childType |= value;
}

......我相信这会有进一步的改进,但这是(半)可读的版本。

3 个答案:

答案 0 :(得分:7)

如果你碰巧知道了这些位的当前值,你可以异或:

  0110 1011 1111
^ 0000 1100 0000

= 0110 0111 1111

(其中1100需要首先计算为当前位和所需位之间的XOR。)

当然,这仍然是2次操作。不同之处在于,您可以在某些情况下预先计算第一个XOR。

除了这种特殊情况,没有别的办法。您基本上需要表示3种状态:设置为1,设置为0,不要更改。您不能使用单个二进制操作数执行此操作。

答案 1 :(得分:2)

您可能希望使用位字段(如果您希望能够将结构作为一组位字段和同时作为int访问,则可能使用联合字段),类似于以下内容:

struct foo
{  
unsigned int o1:4;  
unsigned int o2:4;  
unsigned int o3:4;   
};  

foo bar;  

bar.o2 = 0b0111; 

不确定它是否转换为比clear / set更高效的机器代码......

答案 2 :(得分:1)

嗯,MMIX中有一个汇编指令:

SETL $1, 0x06BF ; 0110 1011 1111
SETL $2, 0x0070 ; 0000 0111 0000
SETL rM, 0x00F0 ; set mask register

MUX $1,$2,$1 ; result is 0110 0111 1111

但是在C ++中你可能会想到“取消之前的价值”。

int S = 0x6BF;      // starting value:   0110 1011 1111
int M = 0x0F0;      // value mask:       0000 1111 0000
int V = 0x070;      // value:            0000 0111 0000

int N = (S&~M) | V; // new value:        0110 0111 1111

但是由于来自(S&~M)的中间结果0110 0000 1111永远不会存储在任何地方的变量中,我不会真正称之为“取消设置”任何内容。它只是一个按位布尔表达式。具有相同真值表的任何布尔表达式都将起作用。这是另一个:

N = ((S^V) & M) ^ A; // corresponds to Oli Charlesworth's answer

相关真值表:

    S M V  (S& ~M) | V      ((S^V) & M) ^ S
    0 0 0    0 1   0           0   0    0
 *  0 0 1    0 1   1           1   0    0
    0 1 0    0 0   0           0   0    0
    0 1 1    0 0   1           1   1    1
    1 0 0    1 1   1           1   0    1
 *  1 0 1    1 1   1           0   0    1
    1 1 0    0 0   0           1   1    0
    1 1 1    0 0   1           0   0    1
                   ^                    ^
                   |____________________|

标有'*'的行无关紧要,因为它们不会发生(当未设置相应的掩码位时,V中的位将永远不会被设置)。除了那些行,表达式的真值表是相同的。