BMaximus has asked for the wisdom of the Perl Monks concerning the following question:

Hello Fellow Monks,

I'm hoping someone could explain to me on how to use bit flags. I have a situation that requires me to see if a bit on a flag is set. How do you do that using bitwise operators? I've seen the explanation somewhere but I can't seem to find it.

Say I have a 5 bit flag. I know each bit value is 1,2,4,8 and 16 respectively. This is from a set in MySQL so I need to determine if the last bit is set within the set even if there are others set within the flag. Do I need to change the number that results from the set to binary first or can I use a bit opperator to see if the last bit is flipped or not?

Update: Changed the question a bit to reflect what I meant to avoid more confusion.

Thanks,

BMaximus

Replies are listed 'Best First'.
Re: Flipping bits and using flags
by Limbic~Region (Chancellor) on Mar 23, 2006 at 21:09 UTC
    BMaximus,
    You had me all up until the point where you said you had a 5 bit flag at positions 1, 2, 4, 8, and 16 respectively. The handful of times I have used bitstrings - I have used vec and just looked at the literal bit of the string to see if it was set or not.
    print vec($bitstr, $pos, 1) ? 'on' : 'off';
    This assumes that bit 6 is at pos 5 since $pos is really an offset (0 based). You can also use pack and unpack [bB] format to convert back and forth between strings like '01100111' and bitstrings.

    While what my reply has little to nothing to do with what you are doing with MySQL and sets, I didn't want others to be scared of bitstrings just because they sound scary. They can be quite simple.

    Cheers - L~R

      I think you misunderstood me and I can see why. I said position when I meant bit value. In binary the position values are (if I understand this right) are 1,2,4,8 and 16 respectively. I'll correct my question to reflect what I really meant.

      BMaximus
Re: Flipping bits and using flags
by davidrw (Prior) on Mar 23, 2006 at 20:51 UTC
    should be able to just use the & bitwise operator (see perlop) on the ascii (ord) value of the flag:
    my $flag = "\r"; foreach my $bit ( 0 .. 4 ){ printf "Bit %d: %s\n", $bit+1, ( ord($flag) & 2**$bit ? "on" : "off" +); }

      I'd be inclined to use 1 << $bit instead. The result is exactly the same, but shift has bit semantics rather than the arithmetic semantics of **.

      For the OP's question ord is probably not required. The implication of the OP's post is that there is already a $flags variable so the test is:

      $flags & (1 << $bit) ? "on" : "off"

      It is important for OP to note that $bit ranges from 0 (least significant bit) to 4 (most significant of 5 bits) and that the expression 1 << $bit generates the values 1, 2, 4, 8 and 16.


      DWIM is Perl's answer to Gödel
Re: Flipping bits and using flags
by wazzuteke (Hermit) on Mar 24, 2006 at 00:44 UTC
    I hope I'm not confused with youre update, but I think I may understand what you are asking. Say, for example, your bit flag is set to my $bit_flag = 0b101; #5. And, you need to find out if the given bit flag has a bit flipped within the list of qw( 1 2 4 8 ). If this is the case (and I apologize if I am still not understanding), you could:
    #!/usr/bin/perl use strict; use warnings; my $bit_flag = 5; #0b101 my @bit_list = qw( 1 2 4 8 16 ); # Assuming you want to test for truth of my above statment # later in the application, binary 1/0 will work fine my $bit_flipped = ( grep{ $bit_flag & $_ } @bit_list ) ? 1 : 0; # Continue on with the application
    Runing a bitwise and will simply tell you that there are some bits that are up in both the $bit_flat as within one of the values in the @bit_list.

    CPAN has a good resource that might better explain what I am trying to say, while detailing the differences between the bitwise and (&) and the bitwise or (|).

    Again, I hope I am understanding your question well enough to answer it. Good luck!

    ---hA||ta----
    print$_ for(map{chr($_)}split(/\s+/,join(/\B?:\w+[^\s+]/,<DATA>))); __DATA__ 67 111 100 101 32 80 101 114 108
      Hey this is much appreciated but you missed the mark on this one unfortunately. In MySQL the sets can either be inserted by name or by inserting a number that corresponds to the numeric value given that element that is in the set. For example:

      In a table I have a field of type SET that is defined this way:

      "Dog","Cat","Bird","Reptile","Horse","Fish"

      MySQL allows you to either insert the set as one or many of the elements.

      A person says they have a Dog, Cat and a Fish. So it can be inserted as a list: 'Dog','Cat','Fish'

      The other way it can be inserted is by a number that represents the binary value. MySQL assigns a numberic value to each of the set's elements like so:

      "Dog" => 1, "Cat" => 2, "Bird" => 4, "Reptile" => 8, "Horse" => 16, "Fish" => 32

      Basically it's like a bit flag. MySQL allows up to 64 elements in a set. So if I were to insert by number I would add up the values for the elements. 1+2+4 = 7, when you insert 7, MySQL takes it as inserting 'Dog','Cat','Bird'. It's like using a binary flag.

      BMaximus
Re: Flipping bits and using flags
by BrowserUk (Pope) on Mar 24, 2006 at 05:12 UTC

    One thing you will need to be aware of when dealing with MySQL SET types is that unless you are using a perl that is built for either a 64-bit platform, or has been built to enable 64-bit ints, if you attempt to manipulate them using the math operators, you are likely to come unstuck, as they will probably be converted to doubles and they only support 53-bits of precision.

    If your on a 32-bit platform you should treat them as 8 byte strings (carefully avoiding allowing them to get treated as unicode!), and only use vec to access the bits. The downside of that is that depending upon your platform vec may index the bits in a different order to the way they are described by the MySQL docs. That's most likely to be the case if (for example), the MySQL server was running on a Solaris box and your program on Intel; or vice versa; or many other combinations.

    The only sure way to know will be to experiment with setting a few bits, insert them into your DB and then query the DB via it's own client to see which interpretation it puts upon the bits you set.

    The alternative would be to use pack & unpack with either 'b64' or 'B64' as appropriate to expand the bits into an array. If your program needs to run cross platform, you could do something like:

    use Config; my $setTemplate; if( $Config{ byteorder } eq '1234' ) { $setTemplate = 'B64'; } elsif( $Config{ byteorder } eq '3412' ) { $setTemplate = 'b64'; } else { die "What kind of cpu are you using for dogs sake:)!"; } ...

    Note: The above is untested and I probably have guessed wrong about which template goes with which ordering!


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.