¿Cómo obtener una lista de números decimales "ocultos" en números binarios en PHP?

firstly, I very much apologize for my poorly written title of this question. So please someone with native English, change the title appropriately. My question is rather simple, it follows:

I am using integer to store multiple types of one item. For example:

TYPE A = 1
TYPE B = 2
TYPE C = 4
TYPE D = 8
TYPE E = 16
etc...

Now the item in DB has type value 14, that means that it has been assigned to TYPE B+C+D. If it would have type value for example 9, it would mean it has been assigned to TYPE A+D.

I need a function which I would supply with single type integer and this function would return array of integer types.

I could iterate through all the integers and compare them with the number, but that's what I am using now, but I am looking for some more effective way, if it exists ?

Gracias de antemano por su ayuda.

preguntado el 08 de noviembre de 11 a las 13:11

It sounds like you're trying to use a máscara de bits; typically you "retrieve" values via binary AND operations. -

4 Respuestas

¿Cómo es esto? http://codepad.org/AzgdPsL1

Para explicar lo que está pasando:

  1. Creo un $types array of all the valid types that exist between the range of 1 to $max_bit Bits.
  2. Looping while the number is greater than 0, it gets bitwise ANDed with 1. If it turns out that evaluates to true, this means that the LSB is set, so the type a la cabeza de la $type array applies to this number. The current type is added to the return array.
  3. The number is then shifted to the right by one bit.

    <?php
    
    function num2type( $num)
    {
        $max_bit = 5;
        $types = array_combine( range( 1, $max_bit), range( ord( 'A'), ord( 'A') + $max_bit - 1));
    
        $return = array();
        while( $num > 0)
        {
            $current_type = array_shift( $types);
            if( $num & 0x1)
            {
                $return[] = chr( $current_type);
            }
            $num = $num >> 1;
        }
        return $return;
    }
    
    var_dump( num2type( 8)); // array(1) { [0]=> string(1) "D" }
    var_dump( num2type( 31)); 
    var_dump( num2type( 14));
    

Output (for 31):

array(5) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "C"
  [3]=>
  string(1) "D"
  [4]=>
  string(1) "E"
}

Output for 14:

array(3) {
  [0]=>
  string(1) "B"
  [1]=>
  string(1) "C"
  [2]=>
  string(1) "D"
}

respondido 08 nov., 11:17

function check_flag($field, $bit)
{
    return (($field | $bit) === $field) ? TRUE : FALSE;
}

$types = array('A' => 1, 'B' => 2, 'C' => 4, 'D' => 8, 'E' => 16);
$db_value = 14;
var_dump(check_flag($db_value, $types['B']));

... just make sure that you cast the value fetched from the database to integer.

Edit: Now that I read you need all of the types that are set, here's some more logic:

$set = array();
foreach ($types as $key => $value)
    if (check_flag($db_value, $value)) $set[] = $key;

respondido 08 nov., 11:17

$a = 10;
$scan = 1;
$result = array();
while ($a >= $scan){
    if ($a & $scan)
        $result[] = $scan;
        $scan<<=1; //just a bit shift
}
var_dump($result);

respondido 08 nov., 11:17

$scan <<= 1 would surely be better than $scan*=2, ¿No? - Niet the Dark Absol

Here's a function without any loops (mostly done for the fun of it :) ):

function getTypes($int)
{
    $types = array('Type A','Type B','Type C','Type D','Type E');//defining types
    $t = array_reverse(str_split(decbin($int)));//converting $int to an array of bits
    $types = array_slice($types,0,ceil(log($int,2)));//slicing the array to the same size as number of bits in the $int
    $t = array_combine($types,$t);// Creating a new array where types are keys and bits are values
    return array_keys($t,1);// returning an array of keys which have a value of 1
}

However it doesn't mean that it is efficient. If you use a bitmask you better of checking values using bitwise operators like bitwise and (&). For example if you want to check if your integer contains Type D and Type E you should do

if ($integer & 8 & 16)

To check for each individual type I would us a loop with bitshift operator

function getTypes($int)
{
    $result = array();
    $types = array('Type A','Type B','Type C','Type D','Type E');
    foreach($types as $type)
    {
        if ($int & 1)//checking if last bit is 1 (exactly the same as if($int%2) )
            $result[]=$type;
        $int>>=1;//shifting integer's bits to the right (exactly the same as $int = $int / 2)
    }
    return $result;
}

respondido 08 nov., 11:18

Maybe I'm not getting something, but it doesn't work at all. Throws 2 warnings and returns null. - J0HN

My bad, I forgot to slice types array to the correct size. Should work fine now. - Ivan

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.