Reputation: 137
How would one go about writing a macro in C that checks if any bits of a long are set within a range? If all the bits of a long are set within a range?
Could you do something like
#define TEST_ALL(number, high, low) (1 & (number << low))
But that would only check the lowest bit. I understand you could do a for loop and just keep incrementing from low until you get to high checking bit by bit, but I'm wondering if there is a simpler solution with a macro that would be just as effective.
Upvotes: 0
Views: 1751
Reputation: 881263
Let's assume 32-bit unsigned int
for simplicity. You can get a mask of all 1-bits to the right of a given position 32-0 (including that position) with:
#define maskFrom(p) ((p == 32) ? 0xffffffffU : (1U << p) - 1)
You can get a range of bits with some simple bitwise manipulation, assuming p1 >= p2
:
#define maskFromTo(p1, p2) maskFrom(p1) & ~maskFrom(p2)
These macros can be tested with the following complete program:
#include <stdio.h>
#define maskFrom(p) (((p) == 32) ? 0xffffffffU : (1U << (p)) - 1)
#define maskFromTo(p1, p2) maskFrom(p1) & ~maskFrom(p2)
int main(void) {
// Do "right-of" checking.
int p1 = 32;
do {
printf ("%2d: %08x\n", p1, maskFrom(p1));
} while (--p1 > 0);
// Do range checking with four-bit width.
p1 = 32;
do {
printf ("%2d: %08x\n", p1, maskFromTo(p1,p1-4));
} while (--p1 > 3);
return 0;
}
which outputs:
32: ffffffff
31: 7fffffff
30: 3fffffff
29: 1fffffff
28: 0fffffff
27: 07ffffff
: : : : :
7: 0000007f
6: 0000003f
5: 0000001f
4: 0000000f
3: 00000007
2: 00000003
1: 00000001
32: f0000000
31: 78000000
30: 3c000000
29: 1e000000
28: 0f000000
27: 07800000
: : : : :
7: 00000078
6: 0000003c
5: 0000001e
4: 0000000f
Then, to check if any of the bits within that range are set, and
you value with the mask and check if non-zero result:
#define hasSomeBits(val, p1, p2) (((val) & maskFromTo((p1),(p2))) != 0)
Of course, the usual caveats apply when using macros as functions (such as parenthesising all your arguments and ensuring side-effects don't cause issues when arguments are evaluated more than once). You should probably try to avoid it where possible and just use real functions, along the lines of:
#include <stdint.h>
uint32_t maskFrom (const int p) {
// Silently treat invalid input as zero.
if ((p < 0) || (p > 32))
return 0;
// Create lookup table first time through.
static int firstTime = 1;
static uint32_t lookup[33];
if (firstTime) {
firstTime = 0;
lookup[32] = 0xffffffffU;
for (int i = 31; i >= 0; --i) {
lookup[i] = lookup[i + 1] >> 1;
}
}
// Just return the lookup.
return lookup[p];
}
uint32_t maskFromTo (const int p1, const int p2) {
// Allow for unordered parameters.
if (p1 < p2)
return maskFrom (p2) & ~maskFrom (p1);
return maskFrom (p1) & ~maskFrom (p2);
}
int hasSomeBits (const uint32_t val, const uint32_t p1, const uint32_t p2) {
return (val & maskFromTo (p1, p2)) != 0;
}
Upvotes: 2