mpen
mpen

Reputation: 283013

Right shift not working as expected

I need to do some bit twiddling to pull out some data I'm getting from another source.

The data I've got looks like this in binary:

01100011 00000000 00000000 00000000

Which is the little-endian encoding of the decimal number 99.

I want to mask (bitwise-and) it with this value:

01000000 00000000 00000000 00000000

And then shift all the way to the right (30 places).

When I try right-shifting it, however, I get:

00000000 00000000 00000000 00110000

And I can't figure out why.

Here's the code:

function binrep($bin,$len=32) {
    return implode(' ',str_split(str_pad(decbin(hexdec(bin2hex($bin))),$len,'0',STR_PAD_LEFT),8));
}


function mask($data,$pos,$len=1,$size=32) {
    $bits = (1<<$len)-1;
    $shift = $size - $pos - $len;
    $mask = pack('N',$bits << $shift);
    echo binrep($data)."\n".binrep($mask)."\n".binrep(($data&$mask)>>$shift);
    return ($data & $mask) >> $shift;
}

mask(pack('V',99),1);

Where is that value coming from? Why isn't the result

00000000 00000000 00000000 00000001

?

Upvotes: 0

Views: 95

Answers (2)

mpen
mpen

Reputation: 283013

A full solution for anyone that needs to pull out random bits from a 32-bit int:

function decrep($bin) {
    return hexdec(bin2hex($bin));
}

function binrep($bin,$len=32) {
    return implode(' ',str_split(str_pad(decbin(hexdec(bin2hex($bin))),$len,'0',STR_PAD_LEFT),8));
}

function extractBits($data,$pos,$len=1,$size=32) {
    $bits = (1<<$len)-1;
    $shift = $size - $pos - $len;
    $mask = pack('N',$bits << $shift);
    return (decrep($data) & decrep($mask)) >> $shift;
}

Usage:

$result = extractBits($data,1,2); // pulls out the 2nd and 3rd to left bit, returns number in range 0-3

Upvotes: 0

elitechief21
elitechief21

Reputation: 3044

This is a type mismatch. Since ($data & $mask) evaluates to a string, php is implicitly changing the type of $shift to a string to do the operation. And as per the php documentation, this means the >> operator is using ascii values to do the calculation. I added this function to your code:

function decrep($bin)
{
return hexdec(bin2hex($bin));
}

then did the shift to the right using this function binrep(pack('N',((decrep($data) & decrep($mask)) >> $shift))) and this gives the correct result.

Upvotes: 1

Related Questions