Ethan
Ethan

Reputation: 137

C Macro to Test Set Bits in Range

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

Answers (1)

paxdiablo
paxdiablo

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

Related Questions