Reputation: 8847
I've got a sequence of bits, say
0110 [1011] 1111
Let's say I want to set that myddle nybble to 0111
as the new value.
Using a positional masking approach with AND
or OR
, I seem to have no choice but to first unset the original value to 0000
, because if I trying AND
ing or OR
ing against that original value of 1011
, I'm not going to come out with the desired result of 0111
.
Is there another logical operator I should be using to get the desired effect? Or am I locked into 2 operations every time?
The result after kindly assistance was:
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;
}
...I'm sure this will see further improvement, but this is the (semi-)readable version.
Upvotes: 0
Views: 304
Reputation: 88155
Well, there's an assembly instruction in MMIX for this:
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
But in C++ here's what you're probably thinking of as 'unsetting the previous value'.
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
But since the intermediate result 0110 0000 1111 from (S&~M)
is never stored in a variable anywhere I wouldn't really call it 'unsetting' anything. It's just a bitwise boolean expression. Any boolean expression with the same truth table will work. Here's another one:
N = ((S^V) & M) ^ A; // corresponds to Oli Charlesworth's answer
The related truth tables:
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
^ ^
|____________________|
The rows marked with '*' don't matter because they won't occur (a bit in V will never be set when the corresponding mask bit is not set). Except for those rows, the truth tables for the expressions are the same.
Upvotes: 1
Reputation: 272467
If you happen to already know the current values of the bits, you can XOR:
0110 1011 1111
^ 0000 1100 0000
= 0110 0111 1111
(where the 1100
needs to be computed first as the XOR between the current bits and the desired bits).
This is, of course, still 2 operations. The difference is that you could precompute the first XOR in certain circumstances.
Other than this special case, there is no other way. You fundamentally need to represent 3 states: set to 1, set to 0, don't change. You can't do this with a single binary operand.
Upvotes: 7
Reputation: 1281
You may want to use bit fields (and perhaps unions if you want to be able to access your structure as a set of bit fields and as an int at the same time) , something along the lines of:
struct foo
{
unsigned int o1:4;
unsigned int o2:4;
unsigned int o3:4;
};
foo bar;
bar.o2 = 0b0111;
Not sure if it translates into more efficient machine code than your clear/set...
Upvotes: 2