If I understand your question, and you want to be able to extract the value of the N
number of bits (from 1
to sizeof(type) * CHAR_BIT
) beginning at position P
(from 0
to sizeof(type) * CHAR_BIT - 1
) then you can extract that subset of bits with:
/** extract N bits from value starting at position P,
* counting from 0 for the least significant bit
*/
unsigned nbitsatp (unsigned value, unsigned N, unsigned P)
{
/* mask is N 1's bits */
unsigned mask = ~0u >> ((sizeof mask * CHAR_BIT) - N);
return (value >> (P - N + 1)) & mask;
}
(note: for 4-byte unsigned
, N
ranges from 1
to 32
while P
is zero-based ranging from 0
to 31
)
Above, the mask
begins with all bits 1
(~0u
) and then shifts off the total number of bits minus N
(leaving N
1's bits). The value
is then shifted by P - N + 1
so that you AND
the desired number of bits at position P
with the corresponding number of 1's bits. Since both are shifted so the values begin at the least significant bit, the result is the value of the N
bits at position P
.
This avoids having to hardcode the number and individual bit positions for each range of bits you want.
In your example you want to extract the first 3 bits from 0111000000000000
(28672
) which would be the 3 bits N == 3
at positon P == 15
, the result being 011
(3
).
A Short Example
The example below uses unsigned
as the type, which so long as it is at least 2-bytes on your hardware will be more than sufficient for uint16_t
type.
#include <stdio.h>
#include <limits.h>
/** extract N bits from value starting at position P,
* counting from 0 for the least significant bit
*/
unsigned nbitsatp (unsigned value, unsigned N, unsigned P)
{
/* mask is N 1's bits */
unsigned mask = ~0u >> ((sizeof mask * CHAR_BIT) - N);
return (value >> (P - N + 1)) & mask;
}
int main (void) {
unsigned v, n, p;
fputs ("enter v, n, p : ", stdout); /* prompt for v, n, p */
/* read/validate positive int value */
if (scanf ("%u%u%u", &v, &n, &p) != 3) {
fputs ("error: invalid unsigned integer input.\n", stderr);
return 1;
}
/* output result */
printf ("\nvalue of %u bits at pos %u in %u is : %u\n",
n, p, v, nbitsatp (v, n, p));
}
Example Use/Output
Your specific example of wanting the 3 bits beginning a position 15 of 28672
:
$ ./bin/nbitsatp
enter v, n, p : 28672 3 15
value of 3 bits at pos 15 in 28672 is : 3
Or let's take the first 4 bits at position 15, 0111
(7
):
$ ./bin/nbitsatp
enter v, n, p : 28672 4 15
value of 4 bits at pos 15 in 28672 is : 7
Or the first 5-bits beginning at position 15:
$ ./bin/nbitsatp
enter v, n, p : 28672 5 15
value of 5 bits at pos 15 in 28672 is : 14
Or what about the value for the 9-flags (9 bits, at position 8) which will be all zero in your example:
$ ./bin/nbitsatp
enter v, n, p : 28672 9 8
value of 9 bits at pos 8 in 28672 is : 0
Using Pre-Defined Macros To Retrieve Wanted Bits
A convenient way to use the nbitsatp()
function to retrieve the bits you are interested in is to #define
a macro for each group of bits you want to obtain. For example to get the 3-bits for the data offset, the reserved 4-bits and the 9-bit set of flags, you could define three macros that set the number of bits and position allowing you to simply pass the TCP Header value as an argument, e.g.
/* macros for 3-bit offset, 4-bit reserved, 9-bit flags */
#define HDR_OFFSET(TCPHDRVAL) nbitsatp ((TCPHDRVAL), 3, 15)
#define HDR_RESERVED(TCPHDRVAL) nbitsatp ((TCPHDRVAL), 4, 12)
#define HDR_FLAGS(TCPHDRVAL) nbitsatp ((TCPHDRVAL), 9, 8)
To obtain the bits you want, just call the macro passing the TCP header value as an argument, e.g.
int main (void) {
unsigned v;
fputs ("enter TCP hdr value : ", stdout); /* prompt TCP HDR VAL */
/* read/validate TCP header value */
if (scanf ("%u", &v) != 1) {
fputs ("error: invalid unsigned integer input.\n", stderr);
return 1;
}
/* output result */
printf ("\n data offset bits : %u\n"
" reserved bits : %u\n"
" flag bits : %u\n",
HDR_OFFSET (v), HDR_RESERVED (v), HDR_FLAGS (v));
}
Output
$ /bin/nbitsatp_macro
enter TCP hdr value : 28672
data offset bits : 3
reserved bits : 8
flag bits : 0
If wanted, you can output the padded binary representation for 3
(011
), 8
(1000
) and then the flags (000000000
) in a trivial manner. See binprnpad() function in this answer