23

I want a simple C function which will return true if the n-th bit in a byte is set to1. Otherwise it will return false.

This is a critical function in terms of execution time, so I am thinking of the most optimal way to do that.

alex
  • 479,566
  • 201
  • 878
  • 984
liv2hak
  • 14,472
  • 53
  • 157
  • 270

7 Answers7

40

The following function can do what you need:

int isNthBitSet (unsigned char c, int n) {
    static unsigned char mask[] = {128, 64, 32, 16, 8, 4, 2, 1};
    return ((c & mask[n]) != 0);
}

This assumes 8-bit bytes (not a given in C) and the zeroth bit being the highest order one. If those assumption are incorrect, it simply comes down to expanding and/or re-ordering the mask array.

No error checking is done since you cited speed as the most important consideration. Do not pass in an invalid n, that'll be undefined behaviour.

At insane optimisation level -O3, gcc gives us:

isNthBitSet:    pushl   %ebp
                movl    %esp, %ebp
                movl    12(%ebp), %eax
                movzbl  8(%ebp), %edx
                popl    %ebp
                testb   %dl, mask(%eax)
                setne   %al
                movzbl  %al, %eax
                ret
mask:           .byte   -128, 64, 32, 16, 8, 4, 2, 1

which is pretty small and efficient. And if you make it static and suggest inlining, or force it inline as a macro definition, you can even bypass the cost of a function call.

Just make sure you benchmark any solution you're given, including this one (a). The number one mantra in optimisation is "Measure, don't guess!"

If you want to know how the bitwise operators work, see here. The simplified AND-only version is below.

The AND operation & will set a bit in the target only if both bits are set in the tewo sources. The relevant table is:

AND | 0 1
----+----
 0  | 0 0
 1  | 0 1

For a given char value, we use the single-bit bit masks to check if a bit is set. Let's say you have the value 13 and you want to see if the third-from-least-significant bit is set.

Decimal  Binary
  13     0000 1101
   4     0000 0100 (the bitmask for the third-from-least bit).
         =========
         0000 0100 (the result of the AND operation).

You can see that all the zero bits in the mask result in the equivalent result bits being zero. The single one bit in the mask will basically let the equivalent bit in the value flow through to the result. The result is then zero if the bit we're checking was zero, or non-zero if it was one.

That's where the expression in the return statement comes from. The values in the mask lookup table are all the single-bit masks:

Decimal  Binary
  128    1000 0000
   64    0100 0000
   32    0010 0000
   16    0001 0000
    8    0000 1000
    4    0000 0100
    2    0000 0010
    1    0000 0001

(a) I know how good I am, but you don't :-)

Community
  • 1
  • 1
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 3
    Lookup table, yes! But I'd expect bit 0 to be the least significant bit in the byte, and so would have reversed the order of `mask[]` intialized entries. – hardmath Jan 19 '12 at 04:04
  • Not always, @hardmath, a lot of the comms standards fill their octets from the "left". Like you, I prefer thinking of b0 as the least significant. – paxdiablo Jan 19 '12 at 04:12
  • -1, verbose and error-prone to write, learn about bit shifting. – blueshift Jan 19 '12 at 04:13
  • 1
    @blueshift, you only write it _once,_ or zero times if you simply steal this code :-) And I know about bit shifting, I was probably doing it when you were still a suckling :-) Others had already posted the bitshift solution, I just offered a different approach. – paxdiablo Jan 19 '12 at 04:17
  • A different approach that relies on you entering more code, a correct list of mask values and results in larger, likely slower, code. Especially since speed was a requirement, this answer is inferior and deserves my downvote! – blueshift Jan 19 '12 at 04:21
  • 2
    @blueshift, that's your right, of course. I was simply explaining. And you _never_ assume something is faster or slower, you always check. I'd be more prone to listen to you if you had done so. Benchmarking this function on my machine at optimisation `-O0` (none) got through a million checks in four thousandths of a second, that's two hundred and fifty million times a second (although, granted, it's a grunty octo-core development box). If you need better than that then, by all means, choose a faster solution. Personally, I doubt it would be necessary except in the most extreme cases. – paxdiablo Jan 19 '12 at 07:06
  • 1
    You can safely omit the `!= 0` part in your return statement, since `c & mask[n]` by itself already yields the desired return value. If I'm not mistaken, this will save two of your machine instructions, `setne` and `movzbl`. – Philip Jan 19 '12 at 08:54
  • 1
    @Philip, sometimes I prefer to optimise for readability :-) I'd be surprised if that change would result in changes to the underlying assembly but, just checking, it does. The `testb/setne` is replaced with an `andb` - whether that's an improvement, I couldn't say off the top of my head but it probably is. I though gcc was smarter than that. – paxdiablo Jan 19 '12 at 09:14
  • Based on Agner Fog's documents, I really don't expect a LUT to be any faster than shifting. On anything non-stone age, a shift by any offset is an order of magnitude faster than even an L1 hit, nevermind a miss. – harold Jan 19 '12 at 16:07
  • @paxdiablo - I have a small doubt.what is the exact reason for declaring the array as static inside the function.What would the performance implication if the array is non-static? – liv2hak Apr 28 '13 at 07:58
  • 1
    @liv2hak, making it static gives it static storage duration. That means it's not created anew every time the function is called, instead it creates it once at program startup. – paxdiablo Apr 28 '13 at 08:00
  • Why is this the accepted answer? The bitshift is way faster and the code smaller. Using a LUT is good is the values inside can't be easily computed. And the `mask` array should be named `masks` because it stores the masks but is not a mask itself – Jojolatino May 24 '22 at 18:46
  • @Jojolatino, until you've measured the performance on your target platform, the assertion that one way or another is faster is unknown. C runs on a huge variety of platforms. As I stated, any method you consider should be benchmarked. And properly benchmarked, not by relying on what may be incorrect accepted wisdom. – paxdiablo May 25 '22 at 07:22
26

Just check the value of (1 << bit) & byte. If it is nonzero, the bit is set.

Jon
  • 428,835
  • 81
  • 738
  • 806
12

Let the number be num. Then:

return ((1 << n) & num);
Philip
  • 5,795
  • 3
  • 33
  • 68
Divya
  • 2,594
  • 1
  • 17
  • 29
4
bool isSet(unsigned char b, unsigned char n) { return b & ( 1 << n); }
Keith Nicholas
  • 43,549
  • 15
  • 93
  • 156
  • I understand `unsigned char` for the byte value, but `n`? That seems odd. – Rob Jan 19 '12 at 04:05
  • well n can't be bigger than a byte... it doesn't really matter much – Keith Nicholas Jan 19 '12 at 07:50
  • Since `unsigned char` does not constrain n to only valid values, I would argue it's a [small] "code smell" with regards to readability. Of course, that's debatable and I'll agree it doesn't matter much. – Rob Jan 19 '12 at 16:26
2

Another approach would be

    bool isNthBitSet (unsigned char c, int n) {
      return (1 & (c >> n));
    }
martemiev
  • 418
  • 4
  • 10
1
#include<stdio.h>
int main()
{
   unsigned int n,a;
   printf("enter value for n\n");
   scanf("%u",&n);
   pintf("enter value for a:\n");
   scanf("%u",&a);
   a= a|(((~((unsigned)0))>>(sizeof(int)*8-1))<<n);
   printf("%u\n",a);
}   
Eswar
  • 19
  • 1
-1
#include<stdio.h>

int main() 
{
        int data,bit;
        printf("enter data:");
        scanf("%d",&data);
        printf("enter bit position to test:");
        scanf("%d",&bit);
        data&(1<<bit)?printf("bit is set\n"):printf("bit is clear\n");

   return 0;
}
  • 2
    This code-only-answer requires some explanation in order to make the added value more obvious, in comparisoin to other, older, upvoted and mostly better explained answers (not implying that all other answers are better explained...). – Yunnosch Feb 01 '18 at 13:55