عوامل الأعداد الثنائية في PHP

من موسوعة حسوب
< PHP

تتيح عوامل الأعداد الثنائية تقدير ومعالجة بتات (bits) معينة ضمن العدد الصحيح.

مثال الاسم النتيجة
‎$a & $b And و يُعيد 1 مكان كل بت له القيمة 1 في كلٍّ من ‎$a و ‎$b.
‎$a | $b Or أو (الضمنية) يُعيد 1 مكان كل بت له القيمة 1 في ‎$a أو ‎$b أو كلاهما.
‎$a ^ $b Xor أو (غير الضمنية) يُعيد 1 مكان كل بت له القيمة 1  في ‎$a أو ‎$b وليس كلاهما.
‎~ $a Not النفي يعكس قيمة البتات في ‎$a.
‎$a << $b Shift left الإزاحة إلى اليسار تزيح بتات ‎$a بمقدار ‎$b خطوة إلى اليسار (كل خطوة تعني الضرب بالعدد 2).
‎$a >> $b Shift right الإزاحة إلى اليمين تزيح بتات ‎$a بمقدار ‎$b خطوة إلى اليمين (كل خطوة تعني القسمة على 2).

عملية إزاحة البتات في PHP هي عملية حسابية. يتم تجاهل البتات المزاحة إلى كلا الطرفين. في الإزاحة اليسارية تزاح الأصفار إلى الداخل من جهة اليمين في حين يزاح بت الإشارة خارجًا من جهة اليسار، وهذا يعني أن إشارة المعامل (operand) لا تُحفظ. أما في الإزاحات اليمينية فتزاح نسخ من بت الإشارة من جهة اليسار، وهذا يعني أن إشارة العامل تكون محفوظة.

لضمان الحصول على الأولوية المطلوبة يمكن استخدام الأقواس. فعلى سبيل المثال: في التعبير ‎$a & $b == true تعالج المساواة أولًا ثم معامل البتات ثانيًا، أما في التعبير ‎(‎$a & $b) == true فيعالج معامل البتات أولًا ثم تليها المساواة.

إن كانت العوامل & و | و ^ مطبّقًة على السلاسل النصية، فستجري العملية على قيم ASCII الخاصّة بالحروف التي تكوّن السلسلة النصية، وستكون النتيجة سلسلة نصّية، وفيما عدا ذلك تُحوّل جميع الأنواع إلى أعداد صحيحة وتكون النتيجة عددًا صحيحًا.

إن كان العامل ~ مطبّقًا على سلسلة نصية، فستجري العملية على قيم ASCII الخاصة بالحروف المكوّنة للسلسلة النصية، وستكون النتيجة سلسلة نصيّة، وفيما عدا ذلك ستُعامل جميع الأنواع الأخرى على أنّها أعداد صحيحة.

تعامل جميع الأنواع على أنّها أعداد صحيحة في حال استخدام العاملين << و >>.

تستخدم الإعدادات الخاصة بالتبليغ عن الأخطاء error_reporting قيمًا ثنائية، وتقدّم مثالًا واقعيًا عن طريقة تفعيل أو تعطيل البتات (أي ضبط قيمتها إلى 1 أو 0، وبالتالي ستُفعّل الراية [flag] المرتبطة بهذا البت). لعرض جميع الأخطاء باستثناء الملاحظات، تنصّ التعليمات الموجودة في ملف php.ini على استخدام:

E_ALL & ~E_NOTICE

تبدأ الشيفرة من E_ALL

00000000000000000111011111111111

ثم تأخذ قيمة E_NOTICE

00000000000000000000000000001000

ثم تقلبها إلى ~:

11111111111111111111111111110111

وفي النهاية، تستخدم الشيفرة العامل (&) للعثور على البتات المُفعّلة في كلا القيمتين

00000000000000000111011111110111

يمكن تحقيق النتيجة ذاتها باستخدام العامل XOR (^) للعثور على البتات المُفعّلة في القيمة الأولى أو الثانية

E_ALL ^ E_NOTICE

يمكن استخدام error_reporting لتوضيح آلية تشغيل البتات. فلعرض الأخطاء والأخطاء القابلة للاسترجاع يمكن استخدام الطريقة التالية

E_ERROR | E_RECOVERABLE_ERROR

تجمع العملية بين E_ERROR

00000000000000000000000000000001

و

00000000000000000001000000000000

باستخدام العامل OR ("|") للحصول على البتات المشغلة في إحدى القيمتين

00000000000000000001000000000001

المثال 1: تطبيق العوامل AND و OR و XOR على الأعداد الصحيحة

<?php
/*
 * تجاهل القسم العلوي
 * فالهدف منه تنسيق المخرجات لتظهر بشكل أوضح
 */

$format = '(%1$2d = %1$04b) = (%2$2d = %2$04b)'
        . ' %3$s (%4$2d = %4$04b)' . "\n";

echo <<<EOH
 ---------     ---------  -- ---------
 result        value      op test
 ---------     ---------  -- ---------
EOH;


/*
 * هذه هي الأمثلة
 */

$values = array(0, 1, 2, 4, 8);
$test = 1 + 4;

echo "\n Bitwise AND \n";
foreach ($values as $value) {
    $result = $value & $test;
    printf($format, $result, $value, '&', $test);
}

echo "\n Bitwise Inclusive OR \n";
foreach ($values as $value) {
    $result = $value | $test;
    printf($format, $result, $value, '|', $test);
}

echo "\n Bitwise Exclusive OR (XOR) \n";
foreach ($values as $value) {
    $result = $value ^ $test;
    printf($format, $result, $value, '^', $test);
}
?>

تعطي الأمثلة السابقة المخرجات التالية:

 ---------     ---------  -- ---------
 result        value      op test
 ---------     ---------  -- ---------
 Bitwise AND
( 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)

 Bitwise Inclusive OR
( 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)

 Bitwise Exclusive OR (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)

المثال 2: تطبيق XOR على السلاسل النصية

<?php
echo 12 ^ 9;
// النتيجة 5

echo "12" ^ "9";
// النتيجة هي محرّف الفراغ الخلفي
// backspace (ASCII 8)
// ('1' (ascii 49)) ^ ('9' (ascii 57)) = #8

echo "hallo" ^ "hello";
// النتيجة هي قيم ASCII
//  #0 #4 #0 #0 #0
// 'a' ^ 'e' = #4

echo 2 ^ "3";
// النتيجة هي 1
// 2 ^ ((int)"3") == 1

echo "2" ^ 3;
// النتيجة 1
// ((int)"2") ^ 3 == 1
?>

المثال 3: إزاحة البتات في الأعداد الصحيحة

<?php
/*
 * هذه هي الأمثلة
 */

echo "\n--- BIT SHIFT RIGHT ON POSITIVE INTEGERS ---\n";ق

$val = 4;
$places = 1;
$res = $val >> $places;
p($res, $val, '>>', $places, 'copy of sign bit shifted into left side');

$val = 4;
$places = 2;
$res = $val >> $places;
p($res, $val, '>>', $places);

$val = 4;
$places = 3;
$res = $val >> $places;
p($res, $val, '>>', $places, 'bits shift out right side');

$val = 4;
$places = 4;
$res = $val >> $places;
p($res, $val, '>>', $places, 'same result as above; can not shift beyond 0');


echo "\n--- BIT SHIFT RIGHT ON NEGATIVE INTEGERS ---\n";

$val = -4;
$places = 1;
$res = $val >> $places;
p($res, $val, '>>', $places, 'copy of sign bit shifted into left side');

$val = -4;
$places = 2;
$res = $val >> $places;
p($res, $val, '>>', $places, 'bits shift out right side');

$val = -4;
$places = 3;
$res = $val >> $places;
p($res, $val, '>>', $places, 'same result as above; can not shift beyond -1');


echo "\n--- BIT SHIFT LEFT ON POSITIVE INTEGERS ---\n";

$val = 4;
$places = 1;
$res = $val << $places;
p($res, $val, '<<', $places, 'zeros fill in right side');

$val = 4;
$places = (PHP_INT_SIZE * 8) - 4;
$res = $val << $places;
p($res, $val, '<<', $places);

$val = 4;
$places = (PHP_INT_SIZE * 8) - 3;
$res = $val << $places;
p($res, $val, '<<', $places, 'sign bits get shifted out');

$val = 4;
$places = (PHP_INT_SIZE * 8) - 2;
$res = $val << $places;
p($res, $val, '<<', $places, 'bits shift out left side');


echo "\n--- BIT SHIFT LEFT ON NEGATIVE INTEGERS ---\n";

$val = -4;
$places = 1;
$res = $val << $places;
p($res, $val, '<<', $places, 'zeros fill in right side');

$val = -4;
$places = (PHP_INT_SIZE * 8) - 3;
$res = $val << $places;
p($res, $val, '<<', $places);

$val = -4;
$places = (PHP_INT_SIZE * 8) - 2;
$res = $val << $places;
p($res, $val, '<<', $places, 'bits shift out left side, including sign bit');


/*
 * تجاهل الجزء السفلي،
 * فالهدف منه تنسيق المخرجات لتظهر بشكل أوضح
 */

function p($res, $val, $op, $places, $note = '') {
    $format = '%0' . (PHP_INT_SIZE * 8) . "b\n";

    printf("Expression: %d = %d %s %d\n", $res, $val, $op, $places);

    echo " Decimal:\n";
    printf("  val=%d\n", $val);
    printf("  res=%d\n", $res);

    echo " Binary:\n";
    printf('  val=' . $format, $val);
    printf('  res=' . $format, $res);

    if ($note) {
        echo " NOTE: $note\n";
    }

    echo "\n";
}
?>

تكون مخرجات المثال السابق في جهاز يعمل بنظام 32-بت بالشكل التالي:

--- BIT SHIFT RIGHT ON POSITIVE INTEGERS ---
Expression: 2 = 4 >> 1
 Decimal:
  val=4
  res=2
 Binary:
  val=00000000000000000000000000000100
  res=00000000000000000000000000000010
 NOTE: copy of sign bit shifted into left side

Expression: 1 = 4 >> 2
 Decimal:
  val=4
  res=1
 Binary:
  val=00000000000000000000000000000100
  res=00000000000000000000000000000001

Expression: 0 = 4 >> 3
 Decimal:
  val=4
  res=0
 Binary:
  val=00000000000000000000000000000100
  res=00000000000000000000000000000000
 NOTE: bits shift out right side

Expression: 0 = 4 >> 4
 Decimal:
  val=4
  res=0
 Binary:
  val=00000000000000000000000000000100
  res=00000000000000000000000000000000
 NOTE: same result as above; can not shift beyond 0


--- BIT SHIFT RIGHT ON NEGATIVE INTEGERS ---
Expression: -2 = -4 >> 1
 Decimal:
  val=-4
  res=-2
 Binary:
  val=11111111111111111111111111111100
  res=11111111111111111111111111111110
 NOTE: copy of sign bit shifted into left side

Expression: -1 = -4 >> 2
 Decimal:
  val=-4
  res=-1
 Binary:
  val=11111111111111111111111111111100
  res=11111111111111111111111111111111
 NOTE: bits shift out right side

Expression: -1 = -4 >> 3
 Decimal:
  val=-4
  res=-1
 Binary:
  val=11111111111111111111111111111100
  res=11111111111111111111111111111111
 NOTE: same result as above; can not shift beyond -1


--- BIT SHIFT LEFT ON POSITIVE INTEGERS ---
Expression: 8 = 4 << 1
 Decimal:
  val=4
  res=8
 Binary:
  val=00000000000000000000000000000100
  res=00000000000000000000000000001000
 NOTE: zeros fill in right side

Expression: 1073741824 = 4 << 28
 Decimal:
  val=4
  res=1073741824
 Binary:
  val=00000000000000000000000000000100
  res=01000000000000000000000000000000

Expression: -2147483648 = 4 << 29
 Decimal:
  val=4
  res=-2147483648
 Binary:
  val=00000000000000000000000000000100
  res=10000000000000000000000000000000
 NOTE: sign bits get shifted out

Expression: 0 = 4 << 30
 Decimal:
  val=4
  res=0
 Binary:
  val=00000000000000000000000000000100
  res=00000000000000000000000000000000
 NOTE: bits shift out left side


--- BIT SHIFT LEFT ON NEGATIVE INTEGERS ---
Expression: -8 = -4 << 1
 Decimal:
  val=-4
  res=-8
 Binary:
  val=11111111111111111111111111111100
  res=11111111111111111111111111111000
 NOTE: zeros fill in right side

Expression: -2147483648 = -4 << 29
 Decimal:
  val=-4
  res=-2147483648
 Binary:
  val=11111111111111111111111111111100
  res=10000000000000000000000000000000

Expression: 0 = -4 << 30
 Decimal:
  val=-4
  res=0
 Binary:
  val=11111111111111111111111111111100
  res=00000000000000000000000000000000
 NOTE: bits shift out left side, including sign bit

تكون مخرجات المثال السابق في جهاز يعمل بنظام 64-بت بالشكل التالي:

--- BIT SHIFT RIGHT ON POSITIVE INTEGERS ---
Expression: 2 = 4 >> 1
 Decimal:
  val=4
  res=2
 Binary:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0000000000000000000000000000000000000000000000000000000000000010
 NOTE: copy of sign bit shifted into left side

Expression: 1 = 4 >> 2
 Decimal:
  val=4
  res=1
 Binary:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0000000000000000000000000000000000000000000000000000000000000001

Expression: 0 = 4 >> 3
 Decimal:
  val=4
  res=0
 Binary:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0000000000000000000000000000000000000000000000000000000000000000
 NOTE: bits shift out right side

Expression: 0 = 4 >> 4
 Decimal:
  val=4
  res=0
 Binary:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0000000000000000000000000000000000000000000000000000000000000000
 NOTE: same result as above; can not shift beyond 0


--- BIT SHIFT RIGHT ON NEGATIVE INTEGERS ---
Expression: -2 = -4 >> 1
 Decimal:
  val=-4
  res=-2
 Binary:
  val=1111111111111111111111111111111111111111111111111111111111111100
  res=1111111111111111111111111111111111111111111111111111111111111110
 NOTE: copy of sign bit shifted into left side

Expression: -1 = -4 >> 2
 Decimal:
  val=-4
  res=-1
 Binary:
  val=1111111111111111111111111111111111111111111111111111111111111100
  res=1111111111111111111111111111111111111111111111111111111111111111
 NOTE: bits shift out right side

Expression: -1 = -4 >> 3
 Decimal:
  val=-4
  res=-1
 Binary:
  val=1111111111111111111111111111111111111111111111111111111111111100
  res=1111111111111111111111111111111111111111111111111111111111111111
 NOTE: same result as above; can not shift beyond -1


--- BIT SHIFT LEFT ON POSITIVE INTEGERS ---
Expression: 8 = 4 << 1
 Decimal:
  val=4
  res=8
 Binary:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0000000000000000000000000000000000000000000000000000000000001000
 NOTE: zeros fill in right side

Expression: 4611686018427387904 = 4 << 60
 Decimal:
  val=4
  res=4611686018427387904
 Binary:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0100000000000000000000000000000000000000000000000000000000000000

Expression: -9223372036854775808 = 4 << 61
 Decimal:
  val=4
  res=-9223372036854775808
 Binary:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=1000000000000000000000000000000000000000000000000000000000000000
 NOTE: sign bits get shifted out

Expression: 0 = 4 << 62
 Decimal:
  val=4
  res=0
 Binary:
  val=0000000000000000000000000000000000000000000000000000000000000100
  res=0000000000000000000000000000000000000000000000000000000000000000
 NOTE: bits shift out left side


--- BIT SHIFT LEFT ON NEGATIVE INTEGERS ---
Expression: -8 = -4 << 1
 Decimal:
  val=-4
  res=-8
 Binary:
  val=1111111111111111111111111111111111111111111111111111111111111100
  res=1111111111111111111111111111111111111111111111111111111111111000
 NOTE: zeros fill in right side

Expression: -9223372036854775808 = -4 << 61
 Decimal:
  val=-4
  res=-9223372036854775808
 Binary:
  val=1111111111111111111111111111111111111111111111111111111111111100
  res=1000000000000000000000000000000000000000000000000000000000000000

Expression: 0 = -4 << 62
 Decimal:
  val=-4
  res=0
 Binary:
  val=1111111111111111111111111111111111111111111111111111111111111100
  res=0000000000000000000000000000000000000000000000000000000000000000
 NOTE: bits shift out left side, including sign bit


تنبيه: قبل الإصدار 7.0 من PHP، كانت إزاحة الأعداد الصحيحة بمقادير تفوق أو تساوي الحد الأقصى الذي يدعمه النظام للأعداد الصحيحة الطويلة long integer أو بأعداد سالبة تؤدي إلى الحصول على سلوك غير معروف. بعبارة أخرى، إن كنت تستخدم الإصدار PHP 5.x فلا تزح الأعداد الصحيحة أكثر من 31 بت في نظام 32-بت، ولا تزح الأعداد الصحيحة أكثر من 63-بت في نظام 64-بت.

لمعالجة الأعداد التي تتجاوز الحد الأقصى المحدد في PHP_INI_MAX استخدم الدوال التي تقدّمها الإضافة gmp

انظر أيضًا

  • الدالة pack()‎ : تحزيم البيانات في سلسلة نصية ثنائية.
  • الدالة unpack()‎  : فك تحزيم البيانات من السلسلة النصية الثنائية.
  • الدالة gmp_and()
  • الدالة gmp_or()
  • الدالة gmp_xor()
  • الدالة gmp_testbit()
  • الدالة gmp_clrbit()‎.

مصادر