http://www.perlmonks.org?node_id=736547

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

I have a loop from 0 to 127. I need to do the following in perl => 1. convert this decimal number (0 to 127) into a binary string of 8 bits 2. split this binary number into 2 bits each like "dd cc bb aa" 3. Check if any 2 consecutive bits is 0 i.e. either dd or cc or bb or aa is equal to zero and update a counter if they are. I tried the following code but it is not working =>
------------------------------- for ($v = 0; $v < 128; $v++) { $bin_num = sprintf("%0b", $v); # This will give me the binary num +ber if ($v < 64) { $cnt++; # Because uptil value 63 "dd" is always going to be +zero } else { if ($bin_num =~ /00/) { print "\n Coming here if string 00 found for value $bin_nu +m \n"; $cnt++; } else { print "\n Coming here if string 00 NOT found for value $bi +n_num \n"; } } -------------------------------
The problem with this code is that it increments the count even if binary value = 01 01 10 01 i.e. bb = 10 and aa = 01 I want it to increment the count only if aa || bb || cc || dd is zero It would have been easier if I could have somehow split the 8 bit binary number into 2 bits each in the format of dd cc bb aa However I don't know how to do that. Will unpack help here? Anywayz would be greatful if someone can help me with this. At the end of the loop the counter should have value 101 and right now it reaches count 107. Thanks.

Replies are listed 'Best First'.
Re: binary string
by toolic (Bishop) on Jan 15, 2009 at 14:10 UTC
    Slightly more Perlish way to do things:
    use strict; use warnings; my $cnt = 0; for my $v (0 .. 127) { if ($v < 64) { $cnt++; } else { my $bin = sprintf '%08b', $v; my @pairs = $bin =~ /\d\d/g; $cnt++ if (grep {$_ eq '00'} @pairs); } } print "$cnt\n"; __END__ 101

    Update: The grep performs the logical "or" check of your 4 bit pairs (to "00"). I changed your sprintf format spec to always print exactly 8 characters.

Re: binary string
by Corion (Patriarch) on Jan 15, 2009 at 13:56 UTC

    Consider looking at the substr function, or just convert your string into strings of two elements by doing a match:

    my @two_bits = ($bin_num =~ /(..)/g);

    Alternatively, consider creating four binary numbers that check for the four two-bit positions and whether they have the appropriate values:

    my %bits = ( dd => 0b11000000, cc => 0b00110000, bb => 0b00001100, aa => 0b00000011, );

    Then, check your number wether $v & $bits{ dd } == 0 or $v & $bits{ dd } == $bits{ dd }.

Re: binary string
by BrowserUk (Patriarch) on Jan 15, 2009 at 14:15 UTC
Re: binary string
by johngg (Canon) on Jan 15, 2009 at 14:48 UTC

    This seems to give the answer you want. I believe you only want to increment the count if one of the 2-bit values is '00', not count every '00'.

    $ perl -le ' -> $count = 0; -> for ( 0 .. 127 ) -> { -> ( $d, $c, $b, $a ) = -> map 0 + $_, sprintf( q{%08b}, $_ ) =~ m{(..)(..)(..)(..)}; -> $count ++ unless $a and $b and $c and $d; -> } -> print $count;' 101 $

    I hope this is useful.

    Cheers,

    JohnGG

Re: binary string
by gone2015 (Deacon) on Jan 15, 2009 at 16:16 UTC

    Your example looks essentially trivial, so I wonder if there's a larger problem that you are trying to solve ?

    The obvious bit-twiddling way to do this is:

    my $b = 7 ; # Number of bits to worry about my $p = ($b + 1) >> 1 ; # Number of pairs of bits to consider my $cnt = 0 ; for my $v (0..((2**$b)-1)) { my $m = 0b11 ; for (1..$p) { if ($v & $m) { $m <<= 2 ; } else { $cnt++ ; last ; } ; } ; } ;
    this will work for $v up to the largest unsigned integer supported on your machine -- though it gets tedious waiting.

    With $b = 7 you could save time by starting at $v == 85 and $cnt = 84, or more generally:

    ... my $iv = 0 ; for (1..$p) { $iv = ($iv << 2) + 1 ; } ; my $cnt = $iv ; for my $v ($iv..((2**$b)-1)) { ...

    If you want to do this for longer bit-strings than will fit in an unsigned integer, then vec could be your friend... but you may have difficulty counting (also be prepared for a long wait).

    If bit-twiddling looks too much like 'C', then:

    my $f = "%0".($b + ($b & 1))."b" ; my $cnt = 0 ; $cnt += sprintf($f, $_) =~ m/^(?:01|10|11)*00/ for 0..((2**$b)-1) ;
    will do the job. Usually with Perl, the avoiding of explicit loops make things faster. In this case, not (on my machine, anyway).

    For limited values of $b you can do things two pairs of bits at a time:

    my $f = "%0".(($b + ($b & 1)) >> 2)."X" ; my $cnt = 0 ; $cnt += sprintf($f, $_) =~ m/[012348C]/ for 0..((2**$b)-1) ;

    Of course, this is all unnecessarily complicated. For $b bits you have 3**($b >> 1) values where none of the bit pairs are zero. I wonder what the real problem is...

Re: binary string
by borisz (Canon) on Jan 15, 2009 at 15:28 UTC
    Assuming you want increment $cnt by one, even if more as one pattern match per byte.
    my $cnt = 0; for my $num ( 0 .. 127 ) { $cnt++ if ( ( $num & 0xc0 ) == 0 || ( $num & 0x30 ) == 0 || ( $num & 0x0c ) == 0 || ( $num & 0x03 ) == 0 ); } print $cnt;
    Boris
Re: binary string
by NetWallah (Canon) on Jan 15, 2009 at 21:14 UTC
    My $0.02:
    perl -e 'for my $d(0..127){ for (0..3){ next unless ($d & 3<<($_+$_))==0; $count++; } }; print qq|\nCount=$count\n|'
    prints:
    Count=160

    I also considered using 'grep' instead of the inner loop - but this is somewhat clearer.

         ..to maintain is to slowly feel your soul, sanity and sentience ebb away as you become one with the Evil.

Re: binary string
by rir (Vicar) on Jan 15, 2009 at 19:16 UTC
    I'm not sure I'm getting how you want to handle the 8th or most significant bit.
    #!/usr/bin/perl use warnings; use strict; sub a_half_nibble_is_00 { my $str = shift; # XXX bounds my @masks = map {chr $_} ( 3, 12, 48, 192 ); # XXX magic local $_; return !! grep { (~($str.'')&$_) eq $_ } @masks; # L::U::first } my @chars = map { chr $_ } (0 .. 127 ); for my $str ( @chars) { my $flag = a_half_nibble_is_00( $str) ? '*' : '' ; printf "%2s%4d >%08b< >%s<$/", $flag, ord($str), ord($str), $str; }

    Be well,
    rir