Okay, I'm twelve, and I am new to PHP, but I created the following -simple- functions:
<?php
function Isdec($num) {
if(round($num)==$num) {
return false;
}
else {
return true;
}
}
function decshiftl($number, $amount){
if(Isdec($number)) {
$decimal = substr($number, (strlen($number) - round($number) + 1));
$decimal*=pow(10,strlen($decimal) - 1);
$Shiftl = ($number << $amount) + (($decimal << $amount) / pow(10,strlen($decimal)));
}
else {
$Shiftl = $number << $amount;
}
return $Shiftl;
}
function decshiftr($number, $amount){
if(Isdec($number)) {
$decimal = substr($number, (strlen($number) - round($number) + 1));
$decimal*=pow(10,strlen($decimal) - 1);
$Shiftr = ($number >> $amount) + (($decimal >> $amount) / pow(10,strlen($decimal)));
}
else {
$Shiftr = $number >> $amount;
}
return $Shiftr;
}
?>
Anyway, it's just shoving parameters in to an equation, but sometimes reinventing the wheel and putting rockets on it is a good thing.
Bitsel İşleçler
Bitsel işleçler, bir tamsayının içindeki belli bitleri 0 ya da 1 yaparlar.
| Örnek | İsim | Sonuç |
|---|---|---|
$a & $b
|
Ve | Hem $a hem de $b'de 1 olan bitler sonuçta 1 yapılır. |
$a | $b
|
Veya | $a veya $b'de 1 olan bitler sonuçta 1 yapılır. |
$a ^ $b
|
Ayrıcalıklı Veya | Hem $a hem de $b'de 0 veya 1 olan bitler sonuçta 0, diğerleri 1 yapılır. |
~ $a
|
Değil | $a'nın 0 olan bitleri 1, 1 olanları 0 yapılır. |
$a << $b
|
Sola kaydırma | $a'nın bitleri $b adım sola kaydırılır (her adım ikiyle çarpma işlemine denktir). |
$a >> $b
|
Sağa kaydırma | $a'nın bitleri $b adım sağa kaydırılır (her adım ikiye bölme işlemine denktir). |
PHP'de bit kaydırma bir aritmetik işlemdir. Kaydırma sonucu her iki uçtan da taşan bitler silinir. Sola kaydırmada sağdan eksilen bitler sıfırlarla doldurulur, bu arada işaret biti soldan taşarak silinir, yani sola kaydırmada terimin işareti korunmaz. Sağa kaldırmada ise işaret biti yerinden oynatılmaz, yani sağa kaydırmada terimin işareti korunur.
Bir işlemin öncelik almasını istiyorsanız yaylı ayraçları kullanın. Örneğin, $a & $b == true işleminde önce eşitlik sonra bitsel VE uygulanırken ($a & $b) == true işleminde ise önce bitsel VE sonra eşitlik uygulanır.
Veri türü dönüşümlerinde dikkatli olun. Eğer sol ve sağ taraf değiştirgelerinin ikisi de dizge ise bitsel işleçler terimlerdeki karakterlerin ASCII değerleri üzerinde işlem yapacaktır.
PHP'nin error_reporting yönergesi gerçekte neler olup bittiğini göstermek
için bitsel değerler kullanır. Sadece bilgilendirmeleri değil hataların
tümünü görmek isterseniz php.ini dosyasında ilgili yere şunu yazın:
E_ALL & ~E_NOTICE
Bu E_ALL için şöyle çalışır:00000000000000000111011111111111Keza E_NOTICE...00000000000000000000000000001000... ve ~ ile ters çevrilerek:11111111111111111111111111110111Son olarak, her iki değer üzerinde etkin bitleri bulmak için VE (&) işlecini kullanır:00000000000000000111011111110111
Bunu sağlamanın bir başka yolu da sadece bir değerde veya diğerinde etkin
bitleri bulmak için XOR (^) kullanmaktır:
E_ALL ^ E_NOTICE
error_reporting yönergesi bitleri etkin kılmayı göstermek için de
kullanılabilir. Sadece hataları ve kurtarılabilir göstermenin yolu:
E_ERROR | E_RECOVERABLE_ERROR
Bu işlem etkin bitleri almak için00000000000000000000000000000001ve00000000000000000001000000000000E_ERROR'ları üzerinde VEYA (|) işlecini kullanır:00000000000000000001000000000001
Örnek 1 - Tamsayılar üzerinde bitsel işlemler
<?php
/*
* Üstteki bölümü yoksayın,
* çıktıyı iyileştirmek için biçemlemekten başka birşey yapmıyor.
*/
$format = '(%1$2d = %1$04b) = (%2$2d = %2$04b)'
. ' %3$s (%4$2d = %4$04b)' . "\n";
echo <<<EOH
--------- --------- -- ---------
sonuç değer işl dnm
--------- --------- -- ---------
EOH;
/*
* Örnekler burada.
*/
$deÄŸerler = array(0, 1, 2, 4, 8);
$dnm = 1 + 4;
echo "\n Bitsel VE \n";
foreach ($deÄŸerler as $deÄŸer) {
$sonuç = $değer & $dnm;
printf($format, $sonuç, $değer, '&', $dnm);
}
echo "\n Bitsel Ayrıcalıklı VEYA \n";
foreach ($deÄŸerler as $deÄŸer) {
$sonuç = $değer | $dnm;
printf($format, $sonuç, $değer, '|', $dnm);
}
echo "\n Bitsel Ayrıcalıklı VEYA (XOR) \n";
foreach ($deÄŸerler as $deÄŸer) {
$sonuç = $değer ^ $dnm;
printf($format, $sonuç, $değer, '^', $dnm);
}
?>
Yukarıdaki örneğin çıktısı:
--------- --------- -- --------- sonuç değer işl dnm --------- --------- -- --------- Bitsel VE ( 0 = 0000) = ( 0 = 0000) & ( 5 = 0101) ( 1 = 0001) = ( 1 = 0001) & ( 5 = 0101) ( 0 = 0000) = ( 2 = 0010) & ( 5 = 0101) ( 4 = 0100) = ( 4 = 0100) & ( 5 = 0101) ( 0 = 0000) = ( 8 = 1000) & ( 5 = 0101) Bitsel Ayrıcalıklı VEYA ( 5 = 0101) = ( 0 = 0000) | ( 5 = 0101) ( 5 = 0101) = ( 1 = 0001) | ( 5 = 0101) ( 7 = 0111) = ( 2 = 0010) | ( 5 = 0101) ( 5 = 0101) = ( 4 = 0100) | ( 5 = 0101) (13 = 1101) = ( 8 = 1000) | ( 5 = 0101) Bitsel Ayrıcalıklı VEYA (XOR) ( 5 = 0101) = ( 0 = 0000) ^ ( 5 = 0101) ( 4 = 0100) = ( 1 = 0001) ^ ( 5 = 0101) ( 7 = 0111) = ( 2 = 0010) ^ ( 5 = 0101) ( 1 = 0001) = ( 4 = 0100) ^ ( 5 = 0101) (13 = 1101) = ( 8 = 1000) ^ ( 5 = 0101)
Örnek 2 - Dizgeler üzerinde bitsel işlemler
<?php
echo 12 ^ 9; // '5' çıktılar
echo "12" ^ "9"; // Gerisilme karakterini çıktılar (ASCII 8)
// ('1' (ascii 49)) ^ ('9' (ascii 57)) = ASCII 8
echo "hallo" ^ "hello"; // Çıktılanan ASCII değerler #0 #4 #0 #0 #0
// 'a' ^ 'e' = #4
echo 2 ^ "3"; // 1 çıktılar
// 2 ^ ((int)"3") == 1
echo "2" ^ 3; // 1 çıktılar
// ((int)"2") ^ 3 == 1
?>
Örnek 3 - Tamsayılarda bit kaydırma
<?php
/*
* Bunlar örnektir.
*/
echo "\n--- POZİTİF TAMSAYILARDA SAĞA BİT KAYDIRMA ---\n";
$dgr = 4;
$hane = 1;
$snç = $dgr >> $hane;
p($snç, $dgr, '>>', $hane, 'işaret bitinin kopyası sola kaydırıldı');
$dgr = 4;
$hane = 2;
$snç = $dgr >> $hane;
p($snç, $dgr, '>>', $hane);
$dgr = 4;
$hane = 3;
$snç = $dgr >> $hane;
p($snç, $dgr, '>>', $hane, 'bitler sağa kaydırıldı');
$dgr = 4;
$hane = 4;
$snç = $dgr >> $hane;
p($snç, $dgr, '>>', $hane, 'yukarıdakiyle aynı; 0\'dan sonrası kaydırılamaz');
echo "\n--- NEGATİF TAMSAYILARDA SAĞA BİT KAYDIRMA ---\n";
$dgr = -4;
$hane = 1;
$snç = $dgr >> $hane;
p($snç, $dgr, '>>', $hane, 'işaret bitinin kopyası sola kaydırıldı');
$dgr = -4;
$hane = 2;
$snç = $dgr >> $hane;
p($snç, $dgr, '>>', $hane, 'bitler sağa kaydırıldı');
$dgr = -4;
$hane = 3;
$snç = $dgr >> $hane;
p($snç, $dgr, '>>', $hane, 'yukarıdakiyle aynı; -1\'dan sonrası kaydırılamaz');
echo "\n--- POZİTİF TAMSAYILARDA SOLA BİT KAYDIRMA ---\n";
$dgr = 4;
$hane = 1;
$snç = $dgr << $hane;
p($snç, $dgr, '<<', $hane, 'sağ taraf sıfırlarla doldurulur');
$dgr = 4;
$hane = (PHP_INT_SIZE * 8) - 4;
$snç = $dgr << $hane;
p($snç, $dgr, '<<', $hane);
$dgr = 4;
$hane = (PHP_INT_SIZE * 8) - 3;
$snç = $dgr << $hane;
p($snç, $dgr, '<<', $hane, 'işaret bitleri dışa kaydırılır');
$dgr = 4;
$hane = (PHP_INT_SIZE * 8) - 2;
$snç = $dgr << $hane;
p($snç, $dgr, '<<', $hane, 'bitler sola kaydırıldı');
echo "\n--- NEGATİF TAMSAYILARDA SOLA BİT KAYDIRMA ---\n";
$dgr = -4;
$hane = 1;
$snç = $dgr << $hane;
p($snç, $dgr, '<<', $hane, 'sağ taraf sıfırlarla doldurulur');
$dgr = -4;
$hane = (PHP_INT_SIZE * 8) - 3;
$snç = $dgr << $hane;
p($snç, $dgr, '<<', $hane);
$dgr = -4;
$hane = (PHP_INT_SIZE * 8) - 2;
$snç = $dgr << $hane;
p($snç, $dgr, '<<', $hane, 'işaret biti dahil, bitler soldan dışa kaydırıldı');
/*
* Bundan sonrasını yoksayın; çıktıyı biçemlemekten başka bir şey yapmıyor.
*/
function p($snç, $dgr, $op, $hane, $ilt = '') {
$format = '%0' . (PHP_INT_SIZE * 8) . "b\n";
printf("İfade: %d = %d %s %d\n", $snç, $dgr, $op, $hane);
echo " Onluk:\n";
printf(" deÄŸer=%d\n", $dgr);
printf(" sonuç=%d\n", $snç);
echo " İkilik:\n";
printf(' deÄŸer=' . $format, $dgr);
printf(' sonuç=' . $format, $snç);
if ($ilt) {
echo " DİKKAT: $ilt\n";
}
echo "\n";
}
?>
Yukarıdaki örneğin 32 bitlik makinelerdeki çıktısı:
--- POZİTİF TAMSAYILARDA SAĞA BİT KAYDIRMA --- İfade: 2 = 4 >> 1 Onluk: değer=4 sonuç=2 İkilik: değer=000000000000000000000000000100 sonuç=000000000000000000000000000010 DİKKAT: işaret bitinin kopyası sola kaydırıldı İfade: 1 = 4 >> 2 Onluk: değer=4 sonuç=1 İkilik: değer=000000000000000000000000000100 sonuç=000000000000000000000000000001 İfade: 0 = 4 >> 3 Onluk: değer=4 sonuç=0 İkilik: değer=000000000000000000000000000100 sonuç=000000000000000000000000000000 DİKKAT: bitler sağa kaydırıldı İfade: 0 = 4 >> 4 Onluk: değer=4 sonuç=0 İkilik: değer=000000000000000000000000000100 sonuç=000000000000000000000000000000 DİKKAT: yukarıdakiyle aynı; 0'dan sonrası kaydırılamaz --- NEGATİF TAMSAYILARDA SAĞA BİT KAYDIRMA --- İfade: -2 = -4 >> 1 Onluk: değer=-4 sonuç=-2 İkilik: değer=111111111111111111111111111100 sonuç=111111111111111111111111111110 DİKKAT: işaret bitinin kopyası sola kaydırıldı İfade: -1 = -4 >> 2 Onluk: değer=-4 sonuç=-1 İkilik: değer=111111111111111111111111111100 sonuç=111111111111111111111111111111 DİKKAT: bitler sağa kaydırıldı İfade: -1 = -4 >> 3 Onluk: değer=-4 sonuç=-1 İkilik: değer=111111111111111111111111111100 sonuç=111111111111111111111111111111 DİKKAT: yukarıdakiyle aynı; -1'dan sonrası kaydırılamaz --- POZİTİF TAMSAYILARDA SOLA BİT KAYDIRMA --- İfade: 8 = 4 << 1 Onluk: değer=4 sonuç=8 İkilik: değer=000000000000000000000000000100 sonuç=000000000000000000000000001000 DİKKAT: sağ taraf sıfırlarla doldurulur İfade: 4611686018427387904 = 4 << 60 Onluk: değer=4 sonuç=4611686018427387904 İkilik: değer=000000000000000000000000000100 sonuç=010000000000000000000000000000 İfade: -9223372036854775808 = 4 << 61 Onluk: değer=4 sonuç=-9223372036854775808 İkilik: değer=000000000000000000000000000100 sonuç=100000000000000000000000000000 DİKKAT: işaret bitleri dışa kaydırılır İfade: 0 = 4 << 62 Onluk: değer=4 sonuç=0 İkilik: değer=000000000000000000000000000100 sonuç=000000000000000000000000000000 DİKKAT: bitler sola kaydırıldı --- NEGATİF TAMSAYILARDA SOLA BİT KAYDIRMA --- İfade: -8 = -4 << 1 Onluk: değer=-4 sonuç=-8 İkilik: değer=111111111111111111111111111100 sonuç=111111111111111111111111111000 DİKKAT: sağ taraf sıfırlarla doldurulur İfade: -9223372036854775808 = -4 << 61 Onluk: değer=-4 sonuç=-9223372036854775808 İkilik: değer=111111111111111111111111111100 sonuç=100000000000000000000000000000 İfade: 0 = -4 << 62 Onluk: değer=-4 sonuç=0 İkilik: değer=111111111111111111111111111100 sonuç=000000000000000000000000000000 DİKKAT: işaret biti dahil, bitler soldan dışa kaydırıldı
Yukarıdaki örneğin 64 bitlik makinelerdeki çıktısı:
--- POZİTİF TAMSAYILARDA SAĞA BİT KAYDIRMA --- İfade: 2 = 4 >> 1 Onluk: değer=4 sonuç=2 İkilik: değer=0000000000000000000000000000000000000000000000000000000000000100 sonuç=0000000000000000000000000000000000000000000000000000000000000010 DİKKAT: işaret bitinin kopyası sola kaydırıldı İfade: 1 = 4 >> 2 Onluk: değer=4 sonuç=1 İkilik: değer=0000000000000000000000000000000000000000000000000000000000000100 sonuç=0000000000000000000000000000000000000000000000000000000000000001 İfade: 0 = 4 >> 3 Onluk: değer=4 sonuç=0 İkilik: değer=0000000000000000000000000000000000000000000000000000000000000100 sonuç=0000000000000000000000000000000000000000000000000000000000000000 DİKKAT: bitler sağa kaydırıldı İfade: 0 = 4 >> 4 Onluk: değer=4 sonuç=0 İkilik: değer=0000000000000000000000000000000000000000000000000000000000000100 sonuç=0000000000000000000000000000000000000000000000000000000000000000 DİKKAT: yukarıdakiyle aynı; 0'dan sonrası kaydırılamaz --- NEGATİF TAMSAYILARDA SAĞA BİT KAYDIRMA --- İfade: -2 = -4 >> 1 Onluk: değer=-4 sonuç=-2 İkilik: değer=1111111111111111111111111111111111111111111111111111111111111100 sonuç=1111111111111111111111111111111111111111111111111111111111111110 DİKKAT: işaret bitinin kopyası sola kaydırıldı İfade: -1 = -4 >> 2 Onluk: değer=-4 sonuç=-1 İkilik: değer=1111111111111111111111111111111111111111111111111111111111111100 sonuç=1111111111111111111111111111111111111111111111111111111111111111 DİKKAT: bitler sağa kaydırıldı İfade: -1 = -4 >> 3 Onluk: değer=-4 sonuç=-1 İkilik: değer=1111111111111111111111111111111111111111111111111111111111111100 sonuç=1111111111111111111111111111111111111111111111111111111111111111 DİKKAT: yukarıdakiyle aynı; -1'dan sonrası kaydırılamaz --- POZİTİF TAMSAYILARDA SOLA BİT KAYDIRMA --- İfade: 8 = 4 << 1 Onluk: değer=4 sonuç=8 İkilik: değer=0000000000000000000000000000000000000000000000000000000000000100 sonuç=0000000000000000000000000000000000000000000000000000000000001000 DİKKAT: sağ taraf sıfırlarla doldurulur İfade: 4611686018427387904 = 4 << 60 Onluk: değer=4 sonuç=4611686018427387904 İkilik: değer=0000000000000000000000000000000000000000000000000000000000000100 sonuç=0100000000000000000000000000000000000000000000000000000000000000 İfade: -9223372036854775808 = 4 << 61 Onluk: değer=4 sonuç=-9223372036854775808 İkilik: değer=0000000000000000000000000000000000000000000000000000000000000100 sonuç=1000000000000000000000000000000000000000000000000000000000000000 DİKKAT: işaret bitleri dışa kaydırılır İfade: 0 = 4 << 62 Onluk: değer=4 sonuç=0 İkilik: değer=0000000000000000000000000000000000000000000000000000000000000100 sonuç=0000000000000000000000000000000000000000000000000000000000000000 DİKKAT: bitler sola kaydırıldı --- NEGATİF TAMSAYILARDA SOLA BİT KAYDIRMA --- İfade: -8 = -4 << 1 Onluk: değer=-4 sonuç=-8 İkilik: değer=1111111111111111111111111111111111111111111111111111111111111100 sonuç=1111111111111111111111111111111111111111111111111111111111111000 DİKKAT: sağ taraf sıfırlarla doldurulur İfade: -9223372036854775808 = -4 << 61 Onluk: değer=-4 sonuç=-9223372036854775808 İkilik: değer=1111111111111111111111111111111111111111111111111111111111111100 sonuç=1000000000000000000000000000000000000000000000000000000000000000 İfade: 0 = -4 << 62 Onluk: değer=-4 sonuç=0 İkilik: değer=1111111111111111111111111111111111111111111111111111111111111100 sonuç=0000000000000000000000000000000000000000000000000000000000000000 DİKKAT: işaret biti dahil, bitler soldan dışa kaydırıldı
32 bitlik sistemlerde 32 bitten fazla sağa kaydırma yapmayın. 32 bitten daha geniş bir sayıyla sonuçlanacak şekilde sola kaydırma yapmayın. PHP_INT_MAX'tan büyük sayılar üzerinde bitsel işlemler yapacaksanız gmp eklentisindeki işlevleri kullanın.
Ayrıca bakınız: pack(), unpack(), gmp_and(), gmp_or(), gmp_xor(), gmp_testbit(), gmp_clrbit()
Bitsel İşleçler
11-Nov-2009 06:10
19-Oct-2009 02:52
Here is an example for bitwise leftrotate and rightrotate.
Note that this function works only with decimal numbers - other types can be converted with pack().
<?php
function rotate ( $decimal, $bits) {
$binary = decbin($decimal);
return (
bindec(substr($binary, $bits).substr($binary, 0, $bits))
);
}
// Rotate 124 (1111100) to the left with 1 bits
echo rotate(124, 1);
// = 121 (1111001)
// Rotate 124 (1111100) to the right with 3 bits
echo rotate(124, -3);
// = 79 (1001111)
?>
04-Jun-2009 03:48
Initially, I found bitmasking to be a confusing concept and found no use for it. So I've whipped up this code snippet in case anyone else is confused:
<?php
// The various details a vehicle can have
$hasFourWheels = 1;
$hasTwoWheels = 2;
$hasDoors = 4;
$hasRedColour = 8;
$bike = $hasTwoWheels;
$golfBuggy = $hasFourWheels;
$ford = $hasFourWheels | $hasDoors;
$ferrari = $hasFourWheels | $hasDoors | $hasRedColour;
$isBike = $hasFourWheels & $bike; # False, because $bike doens't have four wheels
$isGolfBuggy = $hasFourWheels & $golfBuggy; # True, because $golfBuggy has four wheels
$isFord = $hasFourWheels & $ford; # True, because $ford $hasFourWheels
?>
And you can apply this to a lot of things, for example, security:
<?php
// Security permissions:
$writePost = 1;
$readPost = 2;
$deletePost = 4;
$addUser = 8;
$deleteUser = 16;
// User groups:
$administrator = $writePost | $readPosts | $deletePosts | $addUser | $deleteUser;
$moderator = $readPost | $deletePost | $deleteUser;
$writer = $writePost | $readPost;
$guest = $readPost;
// function to check for permission
function checkPermission($user, $permission) {
if($user & $permission) {
return true;
} else {
return false;
}
}
// Now we apply all of this!
if(checkPermission($administrator, $deleteUser)) {
deleteUser("Some User"); # This is executed because $administrator can $deleteUser
}
?>
Once you get your head around it, it's VERY useful! Just remember to raise each value by the power of two to avoid problems
12-May-2009 11:36
Regarding what Bob said about flags, I'd like to point out there's a 100% safe way of defining flags, which is using hexadecimal notation for integers:
<?php
define("f0", 0x1); // 2^0
define("f1", 0x2); // 2^1
define("f2", 0x4); // 2^2
define("f3", 0x8); // 2^3
define("f4", 0x10); // 2^4
define("f5", 0x20); // 2^5
// ...
define("f20", 0x1000000); // 2^20
define("f21", 0x2000000); // 2^21
define("f22", 0x4000000); // 2^22
define("f23", 0x8000000); // 2^23
define("f24", 0x10000000); // 2^24
// ... up to 2^31
?>
I always avoid using decimal notation when I have a large amount of different flags, because it's very easy to misspell numbers like 2^20 (1048576).
26-Apr-2009 07:19
Here is an easy way to use bitwise operation for 'flag' functionality.
By this I mean managing a set of options which can either be ON or OFF, where zero or more of these options may be set and each option may only be set once. (If you are familiar with MySQL, think 'set' datatype).
Note: to older programmers, this will be obvious.
Here is the code:
<?php
function set_bitflag(/*variable-length args*/)
{
$val = 0;
foreach(func_get_args() as $flag) $val = $val | $flag;
return $val;
}
function is_bitflag_set($val, $flag)
{
return (($val & $flag) === $flag);
}
// Define your flags
define('MYFLAGONE', 1); // 0001
define('MYFLAGTWO', 2); // 0010
define('MYFLAGTHREE', 4); // 0100
define('MYFLAGFOUR', 8); // 1000
?>
I should point out: your flags are stored in a single integer. You can store loads of flags in a single integer.
To use my functions, say you wanted to set MYFLAGONE and MYFLAGTHREE, you would use:
<?php
$myflags = set_bitflags(MYFLAGONE, MYFLAGTHREE);
?>
Note: you can pass set_bitflags() as many flags to set as you want.
When you want to test later if a certain flag is set, use e.g.:
<?php
if(is_bitflag_set($myflags, MYFLAGTWO))
{
echo "MYFLAGTWO is set!";
}
?>
The only tricky part is defining your flags. Here is the process:
1. Write a list of your flags
2. Count them
3. Define the last flag in your list as 1 times 2 to the power of <count> minus one. ( I.E. 1*2^(<count>-1) )
3. Working backwards through your list, from the last to the first, define each one as half of the previous one. You should reach 1 when you get to the first
If you want to understand binary numbers, bits and bitwise operation better, the wikipedia page explains it well - http://en.wikipedia.org/wiki/Bitwise_operation.
15-Apr-2009 09:22
For those who are looking for a circular bit shift function in PHP (especially useful for cryptographic functions) that works with negtive values, here is a little function I wrote:
(Note: It took me almost a whole day to get this to work with negative $num values (I couldn't figure out why it sometimes worked and other times didn't), because PHP only has an arithmatic and not a logical bitwise right shift like I am used to. I.e. 0x80000001>>16 will ouputs (in binary) "1111 1111 1111 1111 1000 0000 0000 0000" instead of "0000 0000 0000 0000 1000 0000 0000 0000" like you would expect. To fix this you have to apply the mask (by bitwise &) equal to 0x7FFFFFFF right shifted one less than the offset you are shifting by.)
<?php
function circular_shift($num,$offset) { //Do a nondestructive circular bitwise shift, if offset positive shift left, if negative shift right
$num=(int)$num;
$mask=0x7fffffff; //Mask to cater for the fact that PHP only does arithmatic right shifts and not logical i.e. PHP doesn't give expected output when right shifting negative values
if ($offset>0) {
$num=($num<<$offset%32) | (($num>>(32-$offset%32)) & ($mask>>(31-$offset%32)));
}
elseif ($offset<0){
$offset=abs($offset);
$num=(($num>>$offset%32) & ($mask>>(-1+$offset%32))) | ($num<<(32-$offset%32));
}
return $num;
}
?>
07-Aug-2008 07:03
@greenone - nice function, thanks. I've adapted it for key usage:
<?php
function bitxor($str, $key) {
$xorWidth = PHP_INT_SIZE*8;
// split
$o1 = str_split($str, $xorWidth);
$o2 = str_split(str_pad('', strlen($str), $key), $xorWidth);
$res = '';
$runs = count($o1);
for($i=0;$i<$runs;$i++)
$res .= decbin(bindec($o1[$i]) ^ bindec($o2[$i]));
return $res;
}
?>
07-Apr-2008 09:27
@kendsnyder at gmail dot com
Thanx for your great function. But your function is not 100% correct. It should be:
function safeBitCheck($number,$comparison) {
if( $number < 2147483647 ) {
return ($number & $comparison)==$comparison;
} else {
$binNumber = strrev(base_convert($number,10,2));
$binComparison = strrev(base_convert($comparison,10,2));
for( $i=0; $i<strlen($binComparison); $i++ ) {
if( strlen($binNumber) - 1 <$i || ($binComparison{$i}==="1" && $binNumber{$i}==="0") ) {
return false;
}
}
return true;
}
}
Mind the 'minus 1' on "if( strlen($binNumber) - 1 <$i".
cheers, Bart
07-Dec-2007 02:35
two's complement logical operation for 32-bit.
$x must be (int) when passing it to this function to work properly.
function comp2($x) // 32bit bitwise complement
{
$mask = 0x80000000;
if ($x < 0)
{
$x &= 0x7FFFFFFF;
$x = ~$x;
return $x ^ $mask;
}
else
{
$x = $x ^ 0x7FFFFFFF;
return $x | $mask;
}
}
20-Jun-2007 01:42
To perform bitwise operations on floats, or anything for that matter, in the binary representation of your choice, simply use pack() on the data with the appropriate parameters, then you can operate on the resulting strings. in the end just unpack()
08-Feb-2007 07:19
I found the 31-bit limitation on the bitwise ands to be a bit frustrating in large scale permission control applications. I have a situation involving page-level access with more than 50 pages. I was able to workaround the limitation by adding a loop that dropped 31 bits off of the right until the resource identifier bit is within the first 31.
<?php
$userlevel = $session->userlevel - 0; # the subtraction ensures int type
$pg_code = pow(2,($pg_id-1));
while ($pg_code >= 2147483648) {
$pg_code = $pg_code/pow(2,31);
$userlevel = $session->userlevel/pow(2,31) ;
}
if (!($userlevel - 0 & $pg_code)) {
#if not authorized, show the unauthorized page
header('Location: Unauthorized.php');
exit;
}
?>
12-Jan-2007 07:28
if you use bitwise you MUST make sure your variables are integers, otherwise you can get incorrect results.
I recommend ALWAYS
(int)$var & (int)$var2
This will save you many headaches when troubleshooting a completely illogical result.
14-Nov-2006 12:12
The following function will perform a 32-bit left shift on a 64-bit machine:
<?php
function leftshift32($number, $steps)
{
$binary = decbin($number).str_repeat("0", $steps);
$binary = str_pad($binary, 32, "0", STR_PAD_LEFT);
$binary = substr($binary, strlen($binary) - 32);
return $binary{0} == "1" ? -(pow(2, 31) - bindec(substr($binary, 1))) : bindec($binary);
}
?>
04-Feb-2006 09:34
<?php
// Extract part of a binary data - due to windows system limitations (and this code), bit extracted length($firstbit to $lastbit included) is limited to 31 bits
function sub_bindata($mybindata, $firstbit = 7, $lastbit = 0, $highestbitfirst = true)
{
// 1. Create a bit mask of the right size by triming left and right
// 2. select bits by an AND on $mybindata
// 3. shift right to get only length needed
if($highestbitfirst) return (((0x7FFFFFFF >> (30+$lastbit-$firstbit))<<($lastbit)) & $mybindata) >> ($lastbit);
else return (((0x7FFFFFFF >> (30-$lastbit+$firstbit))<<(30-$lastbit)) & $mybindata) >> (30-$lastbit);
}
?>
02-Feb-2006 10:11
Bitwise operators are swell, but (a & b & c & d) == a
is not a way to test for four-way equality. If a is zero, it'll always be true, and in general it will be true any time a has no bits not also in the other three values.
29-Oct-2005 04:50
For those who were searching for a way to actually rotate the bits of a number, here are some little functions I wrote:
<?php
function bitRotate32($value,$amount) {
if ($amount>0) {
$amount %= 32;
$value = ($value<<$amount) | ($value>>(32-$amount));
} elseif ($amount<0) {
$amount = -$amount%32;
$value = ($value>>$amount) | ($value<<(32-$amount));
}
return $value;
}
function bitRotate($value,$amount,$bits) {
$mask = ($bits<32) ? 0x7fffffff >> (31-$bits) : 0xffffffff;
if ($amount>0) {
$amount %= $bits;
$value = ($value<<$amount) | ($value>>($bits-$amount));
} elseif ($amount<0) {
$amount = -$amount%$bits;
$value = ($value>>$amount) | ($value<<($bits-$amount));
}
return $value & $mask;
}
// test the rotation:
$test = 4123;
for ($i=0; $i<64; $i++) {
$value = bitRotate($test,-$i,8); // rotates 8 bits to the left (-$amount)
echo sprintf("%032b<br/>",$value);
}
?>
27-Oct-2005 02:30
I refer to Eric Swanson's post on Perl VS PHP's implementation of xor.
Actually, this is not an issue with the implementation of XOR, but a lot more to do with the lose-typing policy that PHP adopts.
Freely switching between int and float is good for most cases, but problems happen when your value is near the word size of your machine. Which is to say, 32-bit machines will encounter problems with values that hover around 0x80000000 - primarily because PHP does not support unsigned integers.
using bindec/decbin would address this issue as a work-around to do unsigned-int xor, but here's the real picture (i'm not claiming that this code will perform better, but this would be a better pedagogical code):
<?php
function unsigned_xor32 ($a, $b)
{
$a1 = $a & 0x7FFF0000;
$a2 = $a & 0x0000FFFF;
$a3 = $a & 0x80000000;
$b1 = $b & 0x7FFF0000;
$b2 = $b & 0x0000FFFF;
$b3 = $b & 0x80000000;
$c = ($a3 != $b3) ? 0x80000000 : 0;
return (($a1 ^ $b1) |($a2 ^ $b2)) + $c;
}
$x = 3851235679;
$y = 43814;
echo "<br>This is the value we want";
echo "<br>3851262585";
echo "<br>The result of a native xor operation on integer values is treated as a signed integer";
echo "<br>".($x ^ $y);
echo "<br>We therefore perform the MSB separately";
echo "<br>".unsigned_xor32($x, $y);
?>
This is really foundation stuff, but for those of you who missed this in college, there seems to be something on 2's complement here:
http://www.evergreen.edu/biophysics/technotes/program/2s_comp.htm
27-Oct-2005 12:22
Here're my 32-bit carry-discarding operations for those of you porting encryption algorithms from C.
Be warned that some of these are not very efficient compared to the native operations, especially when called by heavy-duty encryption algorithms - but not discarding the carry bit may not land you the same results you get in C, simply because PHP's bitwise operations were not designed to work on fixed-sized registers.
(If your ported encryption algo still doen't give you the same results, remember to check your Endian-ness!)
function _BF_SHR32 ($x, $bits)
{
if ($bits==0) return $x;
if ($bits==32) return 0;
$y = ($x & 0x7FFFFFFF) >> $bits;
if (0x80000000 & $x) {
$y |= (1<<(31-$bits));
}
return $y;
}
function _BF_SHL32 ($x, $bits)
{
if ($bits==0) return $x;
if ($bits==32) return 0;
$mask = (1<<(32-$bits)) - 1;
return (($x & $mask) << $bits) & 0xFFFFFFFF;
}
function _BF_GETBYTE ($x, $y)
{
return _BF_SHR32 ($x, 8 * $y) & 0xFF;
}
function _BF_OR32 ($x, $y)
{
return ($x | $y) & 0xFFFFFFFF;
}
function _BF_ADD32 ($x, $y)
{
$x = $x & 0xFFFFFFFF;
$y = $y & 0xFFFFFFFF;
$total = 0;
$carry = 0;
for ($i=0; $i<4; $i++) {
$byte_x = _BF_GETBYTE($x, $i);
$byte_y = _BF_GETBYTE($y, $i);
$sum = $byte_x + $byte_y;
$result = $sum & 0xFF;
$carryforward = _BF_SHR32($sum, 8);
$sum = $result + $carry;
$result = $sum & 0xFF;
$carry = $carryforward + _BF_SHR32($sum, 8);
$total = _BF_OR32(_BF_SHL32($result, $i*8), $total);
}
return $total;
}
29-Sep-2005 12:23
note that the shift operators are arithmetic, not logic like in C. You may get unexpected results with negative numbers, see http://en.wikipedia.org/wiki/Bitwise_operation
here's a function to do logic right shifts.
<?php
function lshiftright($var,$amt)
{
$mask = 0x40000000;
if($var < 0)
{
$var &= 0x7FFFFFFF;
$mask = $mask >> ($amt-1);
return ($var >> $amt) | $mask;
}
return $var >> $amt;
}
$val = -10;
printf("arithmetic shift on a negative integer<br>%1\$032b<br>%2\$032b<br>%1\$0d<br>%2\$0d<br>",$val, $val >> 1 );
printf("logic shift on a negative integer<br>%1\$032b<br>%2\$032b<br>%1\$0d<br>%2\$0d<br>",$val, lshiftright($val, 1));
printf("logic shift on a positive integer<br>%1\$032b<br>%2\$032b<br>%1\$0d<br>%2\$0d<br>",-$val, lshiftright(-$val, 1));
?>
gives the output:
arithmetic shift on a negative integer
11111111111111111111111111110110
11111111111111111111111111111011
-10
-5
logic shift on a negative integer
11111111111111111111111111110110
01111111111111111111111111111011
-10
2147483643
logic shift on a positive integer
00000000000000000000000000001010
00000000000000000000000000000101
10
5
01-Sep-2005 12:19
Perl vs. PHP implementation of the ^ operator:
After attempting to translate a Perl module into PHP, I realized that Perl's implementation of the ^ operator is different than the PHP implementation. By default, Perl treats the variables as floats and PHP as integers. I was able to verify the PHP use of the operator by stating "use integer;" within the Perl module, which output the exact same result as PHP was using.
The logical decision would be to cast every variable as (float) when using the ^ operator in PHP. However, this will not yield the same results. After about a half hour of banging my head against the wall, I discovered a gem and wrote a function using the binary-decimal conversions in PHP.
/*
not having much experience with bitwise operations, I cannot tell you that this is the BEST solution, but it certainly is a solution that finally works and always returns the EXACT same result Perl provides.
*/
function binxor($a, $b) {
return bindec(decbin((float)$a ^ (float)$b));
}
//normal PHP code will not yeild the same result as Perl
$result = 3851235679 ^ 43814; //= -443704711
//to get the same result as Perl
$result = binxor(3851235679, 43814); //= 3851262585
//YIPPEE!!!
//to see the differences, try the following
$a = 3851235679 XOR 43814;
$b = 3851235679 ^ 43814; //integer result
$c = (float)3851235679 ^ (float)43814; //same as $b
$d = binxor(3851235679, 43814); //same as Perl!!
echo("A: $a<br />");
echo("B: $b<br />");
echo("C: $c<br />");
echo("D: $d<br />");
19-Apr-2005 02:28
Another practical case...
<?php
header('Content-Type: text/plain') ;
// We want to know the power-2 based numbers of $x
$x = 9124 ;
$n = 1 ;
while ( $x > 0 ) {
if ( $x & 1 == 1 ) {
echo $n, "\n" ;
}
$n *= 2 ;
$x >>= 1 ;
}
// Will output...
// 4
// 32
// 128
// 256
// 512
// 8192
?>
04-Mar-2005 11:13
A bitwise operators practical case :
<?php
// We want to know the red, green and blue values of this color :
$color = 0xFEA946 ;
$red = $color >> 16 ;
$green = ($color & 0x00FF00) >> 8 ;
$blue = $color & 0x0000FF ;
printf('Red : %X (%d), Green : %X (%d), Blue : %X (%d)',
$red, $red, $green, $green, $blue, $blue) ;
// Will display...
// Red : FE (254), Green : A9 (169), Blue : 46 (70)
?>
24-Feb-2005 07:24
Say... you really want to have say... more than 31 bits available to you in your happy bitmask. And you don't want to use floats. So, one solution would to have an array of bitmasks, that are accessed through some kind of interface.
Here is my solution for this: A class to store an array of integers being the bitmasks. It can hold up to 66571993087 bits, and frees up unused bitmasks when there are no bits being stored in them.
<?php
/*
Infinite* bits and bit handling in general.
*Not infinite, sorry.
Perceivably, the only limit to the bitmask class in storing bits would be
the maximum limit of the index number, on 32 bit integer systems 2^31 - 1,
so 2^31 * 31 - 1 = 66571993087 bits, assuming floats are 64 bit or something.
I'm sure that's enough enough bits for anything.. I hope :D.
*/
DEFINE('INTEGER_LENGTH',31); // Stupid signed bit.
class bitmask
{
protected $bitmask = array();
public function set( $bit ) // Set some bit
{
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
$this->bitmask[$key] |= 1 << $bit;
}
public function remove( $bit ) // Remove some bit
{
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
$this->bitmask[$key] &= ~ (1 << $bit);
if(!$this->bitmask[$key])
unset($this->bitmask[$key]);
}
public function toggle( $bit ) // Toggle some bit
{
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
$this->bitmask[$key] ^= 1 << $bit;
if(!$this->bitmask[$key])
unset($this->bitmask[$key]);
}
public function read( $bit ) // Read some bit
{
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
return $this->bitmask[$key] & (1 << $bit);
}
public function stringin($string) // Read a string of bits that can be up to the maximum amount of bits long.
{
$this->bitmask = array();
$array = str_split( strrev($string), INTEGER_LENGTH );
foreach( $array as $key => $value )
{
if($value = bindec(strrev($value)))
$this->bitmask[$key] = $value;
}
}
public function stringout() // Print out a string of your nice little bits
{
$string = "";
$keys = array_keys($this->bitmask);
sort($keys, SORT_NUMERIC);
for($i = array_pop($keys);$i >= 0;$i--)
{
if($this->bitmask[$i])
$string .= sprintf("%0" . INTEGER_LENGTH . "b",$this->bitmask[$i]);
}
return $string;
}
public function clear() // Purge!
{
$this->bitmask = array();
}
public function debug() // See what's going on in your bitmask array
{
var_dump($this->bitmask);
}
}
?>
It treats a positive integer input as a bit, so you don't have to deal with the powers of 2 yourself.
<?php
$bitmask = new bitmask();
$bitmask->set(8979879); // Whatever
$bitmask->set(888);
if($bitmask->read(888))
print 'Happy!\n';
$bitmask->toggle(39393); // Yadda yadda
$bitmask->remove(888);
$bitmask->debug();
$bitmask->stringin("100101000101001000101010010101010
00000001000001");
print $bitmask->stringout() . "\n";
$bitmask->debug();
$bitmask->clear();
$bitmask->debug();
?>
Would produce:
Happy!
array(2) {
[289673]=>
int(65536)
[1270]=>
int(8388608)
}
0000000000000001001010001010010001010100101010100
0000001000001
array(2) {
[0]=>
int(355106881)
[1]=>
int(37970)
}
array(0) {
}
20-Jan-2005 10:12
Note that the ^ operator, unlike in some other languages, is *not* the same as the pow() function.
22-Feb-2004 09:07
For those (like me) who are trying to do bit masking with very large numbers, here is a useful function to do the work for you.
<?php
function isBitSet($bitMask, $bitMap)
{
return (bool) gmp_intval(gmp_div(gmp_and($bitMask, $bitMap),$bitMask));
}
?>
04-Dec-2003 10:30
Hopefully this may help someone understand the fun of Bitwise Operators....
The purpose of this function is to return a value from the GPC (Get, Post and Cookie) and do some basic formatting to it depending on the $VALIDATION value:
<?PHP
function RETURN_SUBMITTED_VALUE ($VARIABLE, $METHOD, $VALIDATION) {
//-------------------------------
// Get the value from the
// relevant submit method...
if ($METHOD == 'POST') {
if (!isset($_POST[$VARIABLE])) $_POST[$VARIABLE] = '';
$VALUE = $_POST[$VARIABLE];
} elseif ($METHOD == 'COOKIE') {
if (!isset($_COOKIE[$VARIABLE])) $_COOKIE[$VARIABLE] = '';
$VALUE = $_COOKIE[$VARIABLE];
} else {
if (!isset($_GET[$VARIABLE])) $_GET[$VARIABLE] = '';
$VALUE = $_GET[$VARIABLE];
}
//-------------------------------
// If necessary strip the slashes.
// the "GPC" means - GET, POST
// COOKIE.
if (ini_get ('magic_quotes_gpc') == true) {
$VALUE = stripslashes($VALUE);
}
//-------------------------------
// Now for the different types
// of VALIDATION's
if (($VALIDATION & 8) == 8) {
$VALUE = (int)$VALUE;
}
if (($VALIDATION & 4) == 4) {
$VALUE = strtolower($VALUE);
}
if (($VALIDATION & 2) == 2) {
$VALUE = strip_tags($VALUE);
}
if (($VALIDATION & 1) == 1) {
$VALUE = trim($VALUE);
}
//-------------------------------
// Finally return the value
return $VALUE;
}
echo RETURN_SUBMITTED_VALUE ('ID', 'GET', 8) . '<br />';
// Convert to an Integer
echo RETURN_SUBMITTED_VALUE ('NAME', 'GET', 3) . '<br />';
// Trim Whitespace and Strip HTML tags
echo RETURN_SUBMITTED_VALUE ('GENDER', 'GET', 6) . '<br />';
// Strip HTML tags and convert to lower case
//-----------------------------------------------
// If this script was loaded under the URL
// index.php?ID=19a&NAME=Krang&GENDER=MalE
// it would print
//
// 19
// Krang
// male
//-----------------------------------------------
?>
For those that don’t understand binary, the numbers you see are not random, they double each time (1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024...) which allows you to mix and match the different function, eg...
1 + 2 = 3 (Trim Whitespace + Strip HTML)
2 + 4 = 6 (Strip HTML + Convert to lower case)
