Perl bitwise operators
last modified November 7, 2026
Bitwise operators work on the individual bits of integers. They are used for flags and permission masks, for extracting fields from packed values such as colours, IP addresses and binary file headers, and for low-level tricks such as testing whether a number is a power of two. Perl has the following bitwise operators:
&, bitwise AND: a bit is set if it is set in both operands;|, bitwise OR: a bit is set if it is set in either operand;^, bitwise XOR: a bit is set if it is set in exactly one operand;~, bitwise NOT: every bit is inverted;<<and>>, left and right shift;&.,|.,^.and~., the string versions, which work on the bytes of strings.
Each operator also has an assignment form, such as |= or
<<=. The bitwise operators should not be confused with the
logical operators &&, || and !,
which work with truth values. The examples were run with 64-bit Perl 5.44 on
Linux.
AND, OR, XOR and NOT
The %b format of printf shows a number in binary, which
makes the effect of the operators visible.
#!/usr/bin/perl use strict; use warnings; use v5.34.0; my ($x, $y) = (0b1100_1010, 0b0101_0110); printf "x %08b %3d\n", $x, $x; printf "y %08b %3d\n", $y, $y; printf "x & y %08b %3d\n", $x & $y, $x & $y; printf "x | y %08b %3d\n", $x | $y, $x | $y; printf "x ^ y %08b %3d\n", $x ^ $y, $x ^ $y; printf "~x %08b %3d\n", ~$x & 0xff, ~$x & 0xff;
The numbers are written as binary literals; underscores can separate digit
groups. The %08b format pads the result to eight digits with
zeros. AND keeps the bits common to both numbers, OR combines them, and XOR
keeps the bits in which they differ. The ~ operator inverts all 64
bits of the number, so we mask the result with & 0xff to keep
only the lowest eight.
$ perl basic.pl x 11001010 202 y 01010110 86 x & y 01000010 66 x | y 11011110 222 x ^ y 10011100 156 ~x 00110101 53
Shift operators
Shifting left by n bits multiplies a number by 2n; shifting right divides it by 2n and drops the remainder. Together with masks, shifts select groups of bits.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
my $n = 13;
for my $i (0 .. 4) {
printf "%2d << %d = %4d %2d >> %d = %2d\n",
$n, $i, $n << $i, $n, $i, $n >> $i;
}
say "1 << 40 = ", 1 << 40;
say "bit 5 of 13: ", (13 >> 5) & 1;
say "bit 2 of 13: ", (13 >> 2) & 1;
my $rgb = 0x3a7fc0;
my ($r, $g, $b) = (($rgb >> 16) & 0xff, ($rgb >> 8) & 0xff, $rgb & 0xff);
say "rgb($r, $g, $b)";
printf "#%06x\n", ($r << 16) | ($g << 8) | $b;
To read bit n, we shift the number right by n places and
take the lowest bit with & 1; 13 is 1101 in
binary, so bit 2 is set. The second part splits an RGB colour stored in one
integer into its three bytes: the red component is in bits 16–23, so we shift
by 16 and mask with 0xff. Shifting the components back and
combining them with OR rebuilds the colour.
$ perl shift.pl 13 << 0 = 13 13 >> 0 = 13 13 << 1 = 26 13 >> 1 = 6 13 << 2 = 52 13 >> 2 = 3 13 << 3 = 104 13 >> 3 = 1 13 << 4 = 208 13 >> 4 = 0 1 << 40 = 1099511627776 bit 5 of 13: 0 bit 2 of 13: 1 rgb(58, 127, 192) #3a7fc0
Bit flags
Flags store several yes/no options in one integer, each option in its own bit. Setting, clearing, toggling and testing a flag each take a single operator.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
use constant {
READ => 1 << 0,
WRITE => 1 << 1,
EXEC => 1 << 2,
ADMIN => 1 << 3,
};
my %names = (READ, 'read', WRITE, 'write', EXEC, 'exec', ADMIN, 'admin');
sub describe {
my $flags = shift;
my @set = map { $names{$_} } grep { $flags & $_ } sort { $a <=> $b } keys %names;
return sprintf '%04b [%s]', $flags, join ',', @set;
}
my $perm = READ | WRITE;
printf "%-14s%s\n", "start:", describe($perm);
$perm |= EXEC;
printf "%-14s%s\n", "add exec:", describe($perm);
$perm &= ~WRITE;
printf "%-14s%s\n", "drop write:", describe($perm);
$perm ^= ADMIN;
printf "%-14s%s\n", "toggle admin:", describe($perm);
$perm ^= ADMIN;
printf "%-14s%s\n", "toggle admin:", describe($perm);
say "can read" if $perm & READ;
say "cannot write" unless $perm & WRITE;
my $need = READ | EXEC;
say "has read and exec" if ($perm & $need) == $need;
Each constant is a different power of two, created with
1 << n. The idioms are:
$perm |= EXECsets a flag;$perm &= ~WRITEclears a flag:~WRITEhas every bit set except the write bit;$perm ^= ADMINtoggles a flag;$perm & READis true if the flag is set;($perm & $need) == $needis true if all flags of a mask are set.
The parentheses in the last test are required, because == binds
more tightly than &. The describe function lists
the names of the flags that are set.
$ perl flags.pl start: 0011 [read,write] add exec: 0111 [read,write,exec] drop write: 0101 [read,exec] toggle admin: 1101 [read,exec,admin] toggle admin: 0101 [read,exec] can read cannot write has read and exec
Unix file mode bits
Unix file permissions are a real-world bit field. The third value returned by
stat holds the file type and the permission bits.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
use Fcntl qw(:mode);
for my $path ('/tmp', '/etc/passwd', '/usr/bin/passwd') {
my $mode = (stat $path)[2] // die "Cannot stat $path: $!";
my $type = S_ISDIR($mode) ? 'dir' : S_ISREG($mode) ? 'file' : 'other';
my $perms = $mode & 07777;
my $str = '';
for my $shift (6, 3, 0) {
my $bits = ($perms >> $shift) & 7;
$str .= ($bits & 4 ? 'r' : '-') . ($bits & 2 ? 'w' : '-')
. ($bits & 1 ? 'x' : '-');
}
my @special;
push @special, 'setuid' if $mode & S_ISUID;
push @special, 'sticky' if $mode & S_ISVTX;
printf "%-16s %-5s %04o %s %s\n", $path, $type, $perms, $str, "@special";
}
The Fcntl module exports the :mode constants and the
S_ISDIR and S_ISREG type tests. The mask
07777 keeps the permission bits and the special bits, which are
printed in octal with %04o. Each group of three bits holds the
read, write and execute permission for the owner, the group and others; we
shift each group down and test its bits. The S_ISUID and
S_ISVTX constants test the setuid and sticky bits.
$ perl mode.pl /tmp dir 1777 rwxrwxrwx sticky /etc/passwd file 0644 rw-r--r-- /usr/bin/passwd file 4755 rwxr-xr-x setuid
Negation and use integer
Perl's bitwise operators work on unsigned 64-bit integers by default. This
affects ~ and the right shift of negative numbers. The
integer pragma switches them to signed arithmetic.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
say ~0;
printf "%x\n", ~0;
say ~5;
say ~5 & 0xff;
{
use integer;
say ~5;
say -16 >> 2;
}
say -16 >> 2;
~0 is the largest unsigned 64-bit number, with all bits set, and
~5 is a huge number rather than -6. Masking with 0xff
gives the expected 8-bit result. Inside the block with use integer,
~5 is -6 in two's complement, and -16 >> 2 is
an arithmetic shift that keeps the sign, giving -4. Outside the block, -16 is
treated as a large unsigned number and the shift produces a positive result.
The pragma is lexically scoped, so it applies only inside its block.
$ perl negate.pl 18446744073709551615 ffffffffffffffff 18446744073709551610 250 -6 -4 4611686018427387900
Counting bits and powers of two
Two classic tricks use the expression $n & ($n - 1), which
clears the lowest set bit of a number.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
sub popcount {
my $n = shift;
my $count = 0;
while ($n) {
$n &= $n - 1;
$count++;
}
return $count;
}
for my $n (0, 1, 7, 255, 1024, 0xdeadbeef) {
printf "%10d %2d %2d\n", $n, popcount($n), unpack '%32b*', pack 'Q', $n;
}
for my $n (1, 6, 64, 100) {
my $pow2 = $n && !($n & ($n - 1));
say "$n is", ($pow2 ? '' : ' not'), " a power of two";
}
The popcount function counts the set bits by clearing the lowest
one until the number is zero, so the loop runs once per set bit. The third
column computes the same value with unpack: pack 'Q'
stores the number as eight bytes, and the %32b* template returns
the checksum of all bits, which is their count. A power of two has exactly one
bit set, so clearing it gives zero; the test also checks that the number is not
zero itself.
$ perl count.pl
0 0 0
1 1 1
7 3 3
255 8 8
1024 1 1
3735928559 24 24
1 is a power of two
6 is not a power of two
64 is a power of two
100 is not a power of two
Subnet calculations
An IPv4 address is a 32-bit number, and a network prefix such as
/22 is a mask with the top 22 bits set. Network calculations are
therefore just AND, OR and NOT.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
use Socket qw(inet_aton inet_ntoa);
sub ip2int { unpack 'N', inet_aton(shift) }
sub int2ip { inet_ntoa(pack 'N', shift) }
my ($ip, $prefix) = split m{/}, shift // '192.168.10.77/22';
my $addr = ip2int($ip);
my $mask = (0xffffffff << (32 - $prefix)) & 0xffffffff;
my $net = $addr & $mask;
my $bcast = $net | (~$mask & 0xffffffff);
say "address: ", int2ip($addr);
say "netmask: ", int2ip($mask);
say "network: ", int2ip($net);
say "broadcast: ", int2ip($bcast);
say "first: ", int2ip($net + 1);
say "last: ", int2ip($bcast - 1);
say "hosts: ", $bcast - $net - 1;
for my $other ('192.168.11.200', '192.168.12.1') {
my $in = (ip2int($other) & $mask) == $net;
say "$other is ", ($in ? 'inside' : 'outside'), " the network";
}
The inet_aton function from the Socket module turns
the dotted address into four bytes, and unpack 'N' reads them as a
32-bit big-endian number. The mask is built by shifting all-ones left by the
number of host bits; because Perl uses 64-bit integers, the result is masked to
32 bits. The network address is the address AND the mask, and the broadcast
address is the network OR the inverted mask. An address belongs to the network
if masking it gives the network address.
$ perl subnet.pl address: 192.168.10.77 netmask: 255.255.252.0 network: 192.168.8.0 broadcast: 192.168.11.255 first: 192.168.8.1 last: 192.168.11.254 hosts: 1022 192.168.11.200 is inside the network 192.168.12.1 is outside the network
String bitwise operators
When both operands are strings, the bitwise operators can also work on the bytes
of the strings. Since Perl 5.28 the bitwise feature, enabled by
use v5.28 or later, separates the two meanings: &,
|, ^ and ~ always treat their operands as
numbers, while &., |., ^. and
~. always treat them as strings.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
say "12" & "10";
say "12" &. "10";
say "AB" |. " ";
say "Hello" ^. (" " x 5);
my $key = 'k3y';
my $msg = 'Attack at dawn';
my $pad = substr($key x (1 + length($msg) / length $key), 0, length $msg);
my $enc = $msg ^. $pad;
say unpack 'H*', $enc;
say $enc ^. $pad;
my ($s1, $s2) = ('perl', 'peal');
my $diff = $s1 ^. $s2;
say "differ at ", join ',', grep { substr($diff, $_, 1) ne "\0" } 0 .. length($diff) - 1;
"12" & "10" is the number 8, but "12" &. "10"
ANDs the characters: '2' (0x32) AND '0' (0x30) is
'0', giving the string 10. Without the feature, the
result depended on whether the values had been used as strings or as numbers,
which was a source of subtle bugs. In ASCII, upper and lower case letters
differ only in the bit 0x20, which is the space character, so OR with spaces
lowercases letters and XOR flips their case. XOR with a repeated key is a toy
cipher: applying the same key twice restores the message. XORing two strings
also shows where they differ, because equal bytes give a zero byte. The string
operators should only be used on byte strings; they die on characters above
255.
$ perl strings.pl 8 10 ab hELLO 2a470d0a50124b520d4b57181c5d Attack at dawn differ at 2
Best practices
- Use the bitwise feature: with
use v5.28or later, numeric and string bitwise operations cannot be confused. - Mask after ~ and <<: results are 64 bits wide; mask
them to the width you need, such as
& 0xffor& 0xffffffff. - Parenthesize comparisons: write
($x & $mask) == $mask, because==has higher precedence than&. - Name your flags: define flags as constants with
1 << ninstead of using bare numbers. - Use integer only when you need signed results: it also changes division and other arithmetic in its scope.
- Prefer binary and octal literals:
0b1010and0755show the bit layout better than decimal numbers.
In this article we have used Perl's bitwise operators. We have combined numbers
with AND, OR, XOR and NOT, used shifts to read and build bit fields, managed bit
flags, decoded Unix file modes, looked at 64-bit negation and
use integer, counted bits, calculated subnet addresses, and worked
with the string bitwise operators.
Source
perlop: Bitwise operators · perlop: Bitwise string operators · integer pragma · feature: bitwise
Author
List all Perl tutorials.