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

I'm having trouble with some bitwise operations. Primarily ^ and ~. I don't seem to have issues with & and |. I was wondering if someone could help me understand what is going wrong here. Much thanks if you can. I'm attaching the applicable code and the results. I can't understand why when complement is used perl will not return a binary string to me as above with the &. It also doesn't seem to do ^ operations properly too. I'm sure this is my fault but after reading Perl docs and message boards for a bit I can't figure it out.
The output is below. icarus# ./test.pl 01001000000111110100111100000000 þÿþþÿþþþþþþÿÿÿÿÿþÿþþÿÿÿÿÿÿÿÿÿÿÿÿ The small test program is below. #!/usr/bin/perl use Validator; my $suspect = '72.31.79.5'; my $suspect2 = '255.255.255.0'; $obj = Validator->new(); $obj->is_hostaddress($suspect, $suspect2); The subroutine from the module is below. sub is_hostaddress { my $self = shift if ref($_[0]); my $ipaddr = shift; my $netmask = shift; return unless defined($ipaddr) && defined($netmask); my $binipaddr = unpack('B32', pack('C4C4C4C4', split(/\./, $ip +addr))); my $binnetmask = unpack('B32', pack('C4C4C4C4', split(/\./, $n +etmask))); my $result = $binipaddr & $binnetmask; my $result2 = $binipaddr | ~ $binnetmask; print $result; print "\n"; print $result2; print "\n"; }

Replies are listed 'Best First'.
Re: Bitwise Complement
by Roy Johnson (Monsignor) on Aug 17, 2007 at 20:34 UTC
    Bitwise operators operate on the "internal" bitstrings that represent numbers or strings, not on strings of 1s and 0s. The reason & and | seem to work is that their characters only differ by a bit. When you complement them, you get those weird characters.

    Instead of unpacking to B, unpack to L (unsigned long), then do your operations. Then use the %032b format of printf to get the bitstring.

    sub is_hostaddress { my $self = shift if ref($_[0]); my $ipaddr = shift; my $netmask = shift; return unless defined($ipaddr) && defined($netmask); my $binipaddr = unpack('L', pack('C4', split(/\./, $ipaddr))); my $binnetmask = unpack('L', pack('C4', split(/\./, $netmask)) +); my $result = $binipaddr & $binnetmask; my $result2 = $binipaddr | ~$binnetmask; # printf "%04x %04x, %04x %04x\n", $binipaddr, $binnetmask, $re +sult, $result2; # printf "%032b\n%032b\n", $binipaddr, $binnetmask; printf "%032b\n%032b\n", $result, $result2; }

    Caution: Contents may have been coded under pressure.
      Thank you for your help and an example of how to make the code work. Thanks to your help and a few others I'm getting a much better grasp on how I need to be attack the full problem with my code (really my understanding of the workings of Perl).
Re: Bitwise Complement
by dwm042 (Priest) on Aug 17, 2007 at 22:10 UTC
    Some of the operators don't work as well when the values they are acting on are represented as binary strings. If we borrow some code from the Perl cookbook (their decimal/binary conversion functions, we can illustrate the issue:
    #!/usr/bin/perl use warnings; use strict; package main; my $suspect = '72.31.79.5'; my $suspect2 = '255.255.255.0'; &is_hostaddress($suspect, $suspect2); sub dec2bin { my $str = unpack("B32", pack("N", shift)); return $str; } sub bin2dec { return unpack("N", pack("B32", substr("0" x 32 . shift, -32))); } sub is_hostaddress { my $ipaddr = shift; my $netmask = shift; return unless defined($ipaddr) && defined($netmask); my $binipaddr = unpack('B32', pack('C4C4C4C4', split(/\./, $ip +addr))); my $binnetmask = unpack('B32', pack('C4C4C4C4', split(/\./, $n +etmask))); print "ip addr = $binipaddr\n"; print "netmask = $binnetmask\n"; # # Convert the mask back to a decimal integer before # attempting to negate. Negating directly gives # Interesting results. # my $ugly = ~ $binnetmask; my $not = dec2bin( ~ bin2dec($binnetmask)); print "badmask = $ugly\n"; print "notmask = $not\n"; my $result = $binipaddr & $binnetmask; my $result2 = $binipaddr | $not; print $result; print "\n"; print $result2; print "\n"; }
    The results being:

    C:\Code>perl masktest.pl ip addr = 01001000000111110100111100000101 netmask = 11111111111111111111111100000000 badmask = ¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨p¨k¨k¨k¨k¨k¨k +¨k¨k notmask = 00000000000000000000000011111111 01001000000111110100111100000000 01001000000111110100111111111111