I have a variable in C with a binary value of '10010100' and I have another variable with the value is '1111'. What I want to achieve is to keep bits 7,6,1,0 intact and insert the second variable in [5..2].
I have been told I could use a mirror. I have done some research and I cannot find the right answer.
If I move bits bitwise, I lose part of the content.
CodePudding user response:
- Use a mask (bitwise AND) to turn the part that should be replaced to the new values to zero.
- Use bitwise OR to put the new value to the zeroed area.
int a = 0x94; /* 10010100 */
int b = 0xf; /* 1111 */
/* do masking here */
/* | put the new value here */
/* | | */
/* v v */
a = (a & ~(0xf << 2)) | (b << 2);
CodePudding user response:
The general solution to this problem is to clear the bits in the destination range with the &
operator and an appropriate mask and set the bits from the second variable, shifted appropriately and potentially masked if it cannot be asserted that no other bits are set:
v1 = (v1 & ~(0xF << 2)) | ((v2 & 0xF) << 2);
If you know that v2
has all bits set in the destination range, you can simplify as:
v1 = v1 | (0xF << 2);
Note however that (v1 & ~(0xF << 2))
uses int
arithmetics, which will extend the mask to the width of v1
if its type is larger than int
. But if the destination included the sign bit of type int
, shifting a 1
bit into this position is undefined behavior. Using an explicit constant would not work either because it would have type unsigned int
and extending it to the type of v1
would also mask the high order bits of v1
if its type is larger than int
. For example:
/* replacing bits 31,30,29,28 */
long long v1 = 0x987654321;
int v2 = 0xF;
v1 = (v1 & ~(0xF << 28)) | ((v2 & 0xF) << 28);
// v1 is now 0x9F7654321 but really undefined behavior
v1 = (v1 & ~0xF0000000) | ((v2 & 0xF) << 28);
// v1 is now 0xF7654321 instead of 0x9F7654321
A similar issue occurs if v2
has a type smaller than that of v1
and must be shifted beyond its own length.
A safer approach would use constants with trailing type markers that match the type of v1
, but this would still not work if a bit has to be shifted into the sign bit of type long long
:
v1 = (v1 & ~(0xFLL << 60)) | ((v2 & 0xFLL) << 60); // undefined behavior
The general solution is to use unsigned long long
constants:
v1 = (v1 & ~(0xFULL << 28)) | ((v2 & 0xFULL) << 28);
The behavior on obsolete non 2's complement architectures is non trivial and will be ignored.
CodePudding user response:
Here is how I would break it down into small pieces.
Note that the 0b
prefix is a non-standard extension, but commonly found on several compilers.
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint8_t a = 0b10010100;
uint8_t b = 0b00001111;
uint8_t keep7610 = a & 0b11000011; // Keep Bits 7, 6, 1, and 0. Set the others to 0
uint8_t insertb = keep7610 | (b << 2); // Add in variable B at position 2-5
printf("Final Answer: 0xX\n", insertb);
return 0;
}
Output
Success #stdin #stdout 0s 5444KB
Final Answer: 0xBC
(0xBC
translates as 0b10111100
, which is what I get when I follow your instructions manually)
CodePudding user response:
Use functions!
unsigned replaceBits(unsigned val, int startBit, int nBits, unsigned newVal)
{
unsigned mask = ((1UL << nBits) - 1) << startBit;
//clear bits
val &= ~mask;
//set new value (adding with mask makes sure that we will not change any other bits.
val |= (newVal << startBit) & mask;
return val;
}
You can also add some checks to make sure that parameters have valid values.
Example: