9

I have these possible bit flags.

1, 2, 4, 8, 16, 64, 128, 256, 512, 2048, 4096, 16384, 32768, 65536

So each number is like a true/false statement on the server side. So if the first 3 items, and only the first 3 items are marked "true" on the server side, the web service will return a 7. Or if all 14 items above are true, I would still get a single number back from the web service which is is the sum of all those numbers.

What is the best way to handle the number I get back to find out which items are marked as "true"?

Nathan
  • 2,941
  • 6
  • 49
  • 80

5 Answers5

7

Use a bit masking operator. In the C language:

 X & 8

is true, if the "8"s bit is set.

You can enumerate the bit masks, and count how many are set.

If it really is the case that the entire word contains bits, and you want to simply compute how many bits are set, you want in essence a "population count". The absolute fastest way to get a population count is to execute a native "popcnt" usually available in your machine's instruction set.

If you don't care about space, you can set up a array countedbits[...] indexed by your value with precomputed bit counts. Then a single memory access computes your bit count.

Often used is just plain "bit twiddling code" that computes bit counts:

(Kernigan's method):

unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v
for (c = 0; v; c++)
{
  v &= v - 1; // clear the least significant bit set
}

(parallel bit summming, 32 bits)

v = v - ((v >> 1) & 0x55555555);                    // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);     // temp
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count

If you haven't seen the bit twiddling hacks before, you're in for a treat.

PHP, being funny, may do funny things with some of this arithmetic.

Ira Baxter
  • 93,541
  • 22
  • 172
  • 341
  • 2
    I would take out the "In the C language"... This is now true in most common languages – Gad May 07 '10 at 22:02
  • 1
    or more generally, `x & (1 << n)` to test if the n-th bit is set – mob May 07 '10 at 22:23
  • @mob Wouldn't it make more sense to do `(x >> n) & 1`, as that would end up as a boolean, 0 or 1? – MiniGod May 09 '13 at 23:14
  • @MiniGod: He asked about the *most efficient* way. – Ira Baxter May 10 '13 at 19:34
  • @IraBaxter: Is mob's solution be more efficient? – MiniGod May 11 '13 at 16:39
  • @MiniGod: No, because it enumerates individual bits, and OP wants the *number* of bits set. Both solutions I have reported process fewer bits. Kernigan's only iterates once per *set* bit. The parallel summing solution is constant time. Even if all you want is to access a single bit, mob's solution requires a shift-and-and; simple x&precomputed_mask avoids the shift; OP knows which bits he wants to test. – Ira Baxter May 11 '13 at 17:09
6
if (7 & 1) { // if bit 1 is set in returned number (7)

}
webbiedave
  • 48,414
  • 8
  • 88
  • 101
  • 1
    So I would just loop through all the possible 14 outcomes above, and see how many if statements report true? Easy enough. If that's the most efficient way, thanks! – Nathan May 07 '10 at 22:46
  • [PHP bitwise operations](http://php.net/manual/en/language.operators.bitwise.php) : "$a & $b : Bits that are set in both $a and $b are set.". To understand, you have to visualize those numbers in their binary representation. 1 is 0001 and 7 is 0100. Since they have no bit in common, it will return 0000 (= false). If for example you were comparing 4 (0100) to 6 (0110) then it would return the common bits, which is 0100, which resolves to true. – Rolf Mar 16 '18 at 16:55
5

Thought the question is old might help someone else. I am putting the numbers in binary as its clearer to understand. The code had not been tested but hope the logic is clear. The code is PHP specific.

define('FLAG_A', 0b10000000000000);  
define('FLAG_B', 0b01000000000000);
define('FLAG_C', 0b00100000000000);
define('FLAG_D', 0b00010000000000);
define('FLAG_E', 0b00001000000000);
define('FLAG_F', 0b00000100000000);
define('FLAG_G', 0b00000010000000);
define('FLAG_H', 0b00000001000000);
define('FLAG_I', 0b00000000100000);
define('FLAG_J', 0b00000000010000);
define('FLAG_K', 0b00000000001000);
define('FLAG_L', 0b00000000000100);
define('FLAG_M', 0b00000000000010);
define('FLAG_N', 0b00000000000001);

function isFlagSet($Flag,$Setting,$All=false){
  $setFlags = $Flag & $Setting;
  if($setFlags and !$All) // at least one of the flags passed is set
     return true;
  else if($All and ($setFlags == $Flag)) // to check that all flags are set
     return true;
  else
     return false;
}

Usage:

if(isFlagSet(FLAG_A,someSettingsVariable)) // eg: someSettingsVariable = 0b01100000000010

if(isFlagSet(FLAG_A | FLAG_F | FLAG_L,someSettingsVariable)) // to check if atleast one flag is set

if(isFlagSet(FLAG_A | FLAG_J | FLAG_M | FLAG_D,someSettingsVariable, TRUE)) // to check if all flags are set
blokeish
  • 571
  • 5
  • 9
2

One way would be to loop through your number, left-shifting it (ie divide by 2) and compare the first bit with 1 using the & operand.

Gad
  • 41,526
  • 13
  • 54
  • 78
1

As there is no definite answer with php code, I add this working example:

// returns array of numbers, so for 7 returns array(1,2,4), etc..

function get_bits($decimal) {
  $scan = 1;
  $result = array();
  while ($decimal >= $scan){
    if ($decimal & $scan) $result[] = $scan;
    $scan<<=1; 
  }
  return $result;
}
Frodik
  • 14,986
  • 23
  • 90
  • 141