I've created a function that enables you to get a range of bits in a byte, counting bit 0 for the least significant bit (LSb) on the right and 7 for the most significant bit (MSb) on the left for code in C or C . However, it's not so straightforward to set bits. This can be generalized for short, int, long etc., but for now I'm sticking with bytes.
Given the following:
#include <stdio.h>
#typedef unsigned char BYTE;
BYTE getByteBits(BYTE n, BYTE b) {
return (n < 8) ? b & ((0x01 << (n 1)) - 1) : 0;
}
where the bits we want to extract are between bits 0 and n, and b is the full byte. If n is 0 only 0 or 1 is returned for this LSb, if n is 1, values between 0 and 3 can be returned, etc., up to n is 7 for the full byte. Of course in the latter case the code is redundant.
This can be called from main() by using something like:
BYTE num = getByteBits(4, myByte);
which will return the numerical value from the 4 lowest bits. However, I've discovered that this can be generalized to:
BYTE num = getByteBits(n - m, myByte >> m);
which will extract the value returned by the bits from m to n, such that 0 <= m <= n <= 7. This is done by shifting myByte by m bits to the right, which is equivalent to shifting the mask to the left.
However, so far I've been unable to do something similar to set bits using a function with three arguments. The best I can do is to create the function:
BYTE setByteBits(BYTE n, BYTE m, BYTE b, BYTE c) {
BYTE mask = ((0x01 << (n 1)) - 1) << m;
return (b & ~mask) | (c & mask);
}
where n and m are the same as before, b is the value of the original byte, and c is the value of some of the bits we want to change. The update byte is returned. This would be called as:
BYTE num = setByteBits(n - m, m, MyByte, c << m);
but 4 rather than 3 arguments are needed, and rather than shifting myByte by m bits to the right, the mask is shifted to the left together with its complement in the function, as given be the 2nd argument m. Although as far as I can see this works correctly, so far all attempts to do something similar as getByteBits() with 3 arguments have failed.
Does anybody have any idea about this? Also I would appreciate if any bugs can be found in the code. Incidentally, for setByteBits() I made use of the link:
How do you set only certain bits of a byte in C without affecting the rest?
Many thanks.
CodePudding user response:
so far I've been unable to do something similar to set bits using a function with three arguments
I do not understand your code, because you use so many m
n
c
b
one letter variables. Maybe try to be more descriptive? You don't have to write short code, there is no performance gain in that.
When n
is the stopping bit position inside the byte, 1 << (n 1)
will give you too big mask. You have to shift 1
of the length of the range of bits, and then that mask shift to the left by the start position. I mixed n
with m
so many times in your code, I do not know which is which.
The following works, at least for tests I tried:
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <limits.h>
typedef unsigned char byte;
#define BYTE_BITS CHAR_BIT
typedef uint_fast8_t bitpos;
/**
* Set bits in the byte `thebyte`
* between the range of bit `rangestart` inclusive to `rangestop` exclusive
* counting from 0 from LSB
* the the range of these bits inside `masktoset`.
*/
byte setByteBits(bitpos rangestart,
bitpos rangestop,
byte thebyte,
byte masktoset) {
assert(rangestop <= BYTE_BITS);
assert(rangestart < rangestop);
const bitpos rangelen = rangestop - rangestart;
const byte rangemask = (1u << rangelen) - 1u;
const byte mask = rangemask << rangestart;
return (thebyte & ~mask) | (masktoset & mask);
}
int main() {
const int tests[][5] = {
{ 4,6,0,0xff,0b00110000 },
{ 4,6,0,0b01010101,0b00010000 },
{ 4,6,0,0b01100101,0b00100000 },
{ 4,6,0xff,0b01000101,0b11001111 },
{ 0,8,0,0xff,0xff },
{ 0,8,0,0xab,0xab },
{ 0,4,0xfa,0xab,0xfb },
{ 0,4,0xab,0xcd,0xad },
{ 4,8,0xab,0xcd,0xcb },
{ 4,8,0xef,0xab,0xaf },
};
for (size_t i = 0; i <sizeof(tests)/sizeof(*tests); i) {
const int *const t = tests[i];
const byte r = setByteBits(t[0],t[1],t[2],t[3]);
fprintf(stderr, "%d (%#02x,%#02x,%#02x,%#02x)->%#02x ?= %#02x\n",
i,t[0],t[1],t[2],t[3],r,t[4]);
assert(r == t[4]);
}
}
CodePudding user response:
Many thanks - I created a new project in Visual Studio 2022, then copied and pasted your code into an empty source file without modifications.
On compiling I get a couple of warning messages as follows:
Rebuild started...
1>------ Rebuild All started: Project: test-binary2, Configuration: Debug x64 ------
1>main.c
1>C:\Users\jeffi\source\repos\test-binary2\main.c(44,25): warning C4477: 'fprintf' : format string '%d' requires an argument of type 'int', but variadic argument 1 has type 'size_t'
1>C:\Users\jeffi\source\repos\test-binary2\main.c(44,25): message : consider using '%zd' in the format string
1>test-binary2.vcxproj -> C:\Users\jeffi\source\repos\test-binary2\x64\Debug\test-binary2.exe
1>Done building project "test-binary2.vcxproj".
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
Then on running I get the following output:
0 (0x4,0x6,00,0xff)->0x30 ?= 0x30
1 (0x4,0x6,00,0x55)->0x10 ?= 0x10
2 (0x4,0x6,00,0x65)->0x20 ?= 0x20
3 (0x4,0x6,0xff,0x45)->0xcf ?= 0xcf
4 (00,0x8,00,0xff)->0xff ?= 0xff
5 (00,0x8,00,0xab)->0xab ?= 0xab
6 (00,0x4,0xfa,0xab)->0xfb ?= 0xfb
7 (00,0x4,0xab,0xcd)->0xad ?= 0xad
8 (0x4,0x8,0xab,0xcd)->0xcb ?= 0xcb
9 (0x4,0x8,0xef,0xab)->0xaf ?= 0xaf
When copying and pasting the output to the page for my reply, the lines are concatenated, so I added a newline after each line of output from both outputs. Other than the extra spaces, is this what you get?