Implementing logical right shifting in PHP

PHP’s bitwise operators << and >> implement Arithmetic Bit Shifting. This means:

  • Left shift (using << operator) fills in zero(s) on the right end of the bitfield, and bit(s) shifted off the left end are discarded;
  • Right shift (using >> operator) discards bit(s) shifted off the right end of the bitfield and copy(ies) of sign bit are shifted in on the left end.

Logical Bit Shifting is different in that on right shifts, zeros are shifted in on the left end of the bitfield, affecting the sign of negative integer.

On 32 bit platforms, -2147483648 is the largest negative integer. Binary representation of this integer gives only the Most Significant Bit ON and all other bits are OFF. Shifting bits of this number $n steps to the right would actually shift in $n copies of the Most Significant Bit (or Sign Bit) and discard the same number of bits from the right.

$n = 30
echo decbin( -2147483648 );          //  10000000000000000000000000000000
 
//Arithmetic Right Shift
echo decbin( -2147483648 >> $n );    //  11111111111111111111111111111110

The same is stated by PHP manual:

Bit shifting in PHP is arithmetic. Bits shifted off either end are discarded. Left shifts have zeros shifted in on the right while the sign bit is shifted out on the left, meaning the sign of an operand is not preserved. Right shifts have copies of the sign bit shifted in on the left, meaning the sign of an operand is preserved – PHP manual

Why Logical Right Shifting in PHP?

In php, bitwise operators are commonly used for manipulating colors, working with user permission management, character encoding/decoding etc and logical right shift is sometimes much needed to simplify operations. Many recommend its addition to php’s core and someone has even put up a request for comment for a new operator. No new operator is needed for Logical Left Shifts because logical and arithmetic left shift are the same.

Implementing logical right sift in PHP:

function logical_right_shift( $int , $shft ) {
    return ( $int >> $shft )   //Arithmetic right shift
        & ( PHP_INT_MAX >> ( $shft - 1 ) );   //Deleting unnecessary bits
}

This function does not validate input. You have to make sure that both of the arguments $int and $shft are integer. Inappropriate arguments may cause unexpected results.

Tests

I have tested logical_right_shift on 32 bit platform, specially with key cases:

  • 0 where all 32 bits are OFF;
  • ~ 0 or -1 where all 32 bits are ON;
  • PHP_INT_MAX – a positive integer with 31 bits ON and just the sign bit OFF, and;
  • ~ PHP_INT_MAX – a negative integer (sign bit ON) with 31 bits OFF.
//Tested With
$int = 0 ;  //0, -1, PHP_INT_MAX, ~PHP_INT_MAX
$shift  = 5;
   
echo 'INT:    '.$int ."\n";
echo 'Before: '.substr(str_repeat('0',32).decbin($int),-32)."\n";
echo 'After:  '.substr(str_repeat('0',32).decbin(logical_right_shift($int,$shift)),-32);
   
 
//INT:    0
//Before: 00000000000000000000000000000000
//After:  00000000000000000000000000000000
 
//INT:    -1
//Before: 11111111111111111111111111111111
//After:  00000111111111111111111111111111
 
//INT:    2147483647        //PHP_INT_MAX
//Before: 01111111111111111111111111111111
//After:  00000011111111111111111111111111
 
//INT:    -2147483648       //~ PHP_INT_MAX
//Before: 10000000000000000000000000000000
//After:  00000100000000000000000000000000

Notes

  • logical_right_shift() was tested to work fine with integers on 32 and 64 bit platforms. It should also work with strings (not tested).
  • Bitwise operators are high performance operators, often faster than arithmetic ones. You should not expect that high performance when using logical_right_shift().
  • The function uses PHP_INT_MAX that is available since PHP 5.0.5.

Leave a Reply