عملگرهای بیت‌بیت ECMAScript

عملگرهای بیت‌اوری در لایه‌های پایین عدد (یعنی 32 بخش که عدد را نمایش می‌دهند) عمل می‌کنند.

دوباره به اعداد صحیح نگاه کنیم

اعداد صحیح ECMAScript دو نوع دارند، یکی اعداد صحیح (که از اعداد مثبت و منفی پشتیبانی می‌کند) و اعداد غیرصحیح (که فقط از اعداد مثبت پشتیبانی می‌کنند). در ECMAScript، تمام مقادیر عددی به صورت پیش‌فرض اعداد صحیح هستند، این به چه معناست؟

اعداد صحیح استفاده از 31 بخش برای نمایش مقادیر اعداد استفاده می‌شود، بخش 32 برای نمایش نشانه عدد استفاده می‌شود، 0 برای اعداد مثبت و 1 برای اعداد منفی. محدوده مقادیر از -2147483648 تا 2147483647 است.

اعداد صحیح دو‌بیتی می‌توانند به دو روش مختلف ذخیره شوند، یک روش برای ذخیره اعداد مثبت و یک روش برای ذخیره اعداد منفی. اعداد مثبت به صورت دو‌بیتی واقعی ذخیره می‌شوند، هر بخش از 31 بخش نشان‌دهنده توان 2 است، از بخش اول (بخش 0) شروع می‌شود و نشان‌دهنده 2 است0، بخش دوم (بخش 1) نشان‌دهنده 2 است1بخش‌های استفاده نشده با 0 پر می‌شوند، یعنی نادیده گرفته می‌شوند. به عنوان مثال، تصویر زیر نمایش‌دهنده روش نمایش عدد 18 است.

عدد دوازده‌ساعته با 32 بیت نمایش داده شده‌است

نسخه دو‌بیتی 18 فقط از 5 بخش اول استفاده کرده است، این‌ها بخش‌های معتبر این عدد هستند. اگر عدد را به رشته دو‌بیتی تبدیل کنید، می‌توانید بخش‌های معتبر را ببینید:

var iNum = 18;
alert(iNum.toString(2));	//نمایش "10010"

این کد فقط "10010" را چاپ می‌کند، نه نمایش 32 بیتی 18. بخش‌های دیگر مهم نیستند، زیرا فقط از 5 بخش اول برای تعیین این عدد دهی استفاده می‌شود. مانند تصویر زیر:

عدد 18 با 5 بیت نمایش داده شده‌است

اعداد منفی نیز به صورت کد دو بیتی ذخیره می‌شوند، اما به صورت کد مکمل دو بیتی استفاده می‌شوند. مراحل محاسبه کد مکمل دو بیتی شامل سه مرحله است:

  1. تعیین نمایش دو بیتی نسخه غیرمنفی عدد (مثلاً برای محاسبه کد مکمل دو بیتی -18، ابتدا باید نمایش دو بیتی 18 را تعیین کنیم)
  2. کد مکمل دو بیتی را محاسبه می‌کنیم، یعنی باید 0 را به 1 و 1 را به 0 تبدیل کنیم
  3. افزودن 1 به کد مکمل دو بیتی

برای تعیین نمایش دو بیتی اعداد منفی، ابتدا باید نمایش دو بیتی اعداد مثبت را بدانیم، به صورت زیر است:

0000 0000 0000 0000 0000 0000 0001 0010

در ادامه، کد مکمل دو بیتی را به صورت زیر محاسبه می‌کنیم:

1111 1111 1111 1111 1111 1111 1110 1101

در نهایت، پس از افزودن 1 به کد مکمل دو بیتی، به صورت زیر است:

1111 1111 1111 1111 1111 1111 1110 1101
                                      1
---------------------------------------
1111 1111 1111 1111 1111 1111 1110 1110

بنابراین، نمایش دو بیتی -18 به صورت 1111 1111 1111 1111 1111 1111 1110 1110 است. به خاطر داشته باشید که در هنگام کار با اعداد صحیح مثبت، توسعه‌دهندگان نمی‌توانند به بیت 31 دسترسی داشته باشند.

جالب است که اعداد صحیح منفی به عنوان رشته‌های دو بیتی تبدیل می‌شوند، ECMAScript آن‌ها را به صورت کد تبدیل اعداد دو بیتی نمایش نمی‌دهد، بلکه به صورت کد استاندارد دو بیتی اعداد صحیح مثبت با افزودن علامت منفی به آن‌ها نمایش می‌دهد. به عنوان مثال:

var iNum = -18;
alert(iNum.toString(2));	// نمایش "-10010"

این کد "-10010" را به نمایش می‌گذارد، نه کد تبدیل اعداد دو بیتی، تا از دسترسی به بیت 31 جلوگیری شود. برای راحتی، ECMAScript به صورت ساده‌ای با اعداد صحیح برخورد می‌کند، به طوری که توسعه‌دهندگان نیازی به اهمیت دادن به استفاده از آن‌ها ندارند.

از سوی دیگر، اعداد صحیح غیرصحیح بیت آخر را به عنوان یک بیت دیگر در نظر می‌گیرند. در این حالت، بیت 32 نشان‌دهنده علامت عدد نیست، بلکه ارزش 2 دارد.31به دلیل این بیت اضافی، محدوده اعداد صحیح غیرصحیح از 0 تا 4294967295 است. برای اعداد صحیح کمتر از 2147483647، اعداد صحیح غیرصحیح مانند اعداد صحیح مثبت به نظر می‌رسند، در حالی که اعداد صحیح بزرگتر از 2147483647 باید از بیت 31 استفاده کنند (در اعداد صحیح مثبت، این بیت همیشه 0 است).

وقتی اعداد صحیح غیرصحیح به عنوان رشته تبدیل می‌شوند، تنها بیت‌های معتبر آن‌ها بازمی‌گردانده می‌شود.

توجه:تمام علامت‌های عددی صحیح به صورت پیش‌فرض به عنوان اعداد صحیح مثبت ذخیره می‌شوند. تنها عملگرهای بیت ECMAScript می‌توانند اعداد صحیح غیرصحیح ایجاد کنند.

عملیات بیت NOT

عملیات بیت NOT با علامت منفی (~) نشان داده می‌شود، که یکی از عملگرهای محاسباتی دو بیتی است که در ECMAScript وجود دارد.

عملیات بیتی NOT شامل سه مرحله است:

  1. تغییر عدد به عدد 32 بیتی
  2. تغییر عدد دودویی به کد معکوس آن
  3. تغییر عدد دودویی به عدد اعشاری

مثلاً:

var iNum1 = 25;		// 25 برابر با 00000000000000000000000000011001 است
var iNum2 = ~iNum1;	// به 11111111111111111111111111100110 تبدیل می‌شود
alert(iNum2);		// خروجی "-26"

عملیات بیتی NOT در واقع منفی کردن عدد است و سپس کم کردن 1، بنابراین 25 به -26 تبدیل می‌شود. می‌توانید به روش زیر نیز به نتیجه برسید:

var iNum1 = 25;
var iNum2 = -iNum1 -1;
alert(iNum2);	// خروجی -26

عملیات بیتی AND

عملیات بیتی AND با نشانه (&) نشان داده می‌شود و مستقیماً روی فرم دودویی اعداد عمل می‌کند. آن را با قوانین زیر در موقعیت‌های مشابه دو عدد انجام می‌دهد:

دوددهای عدد در عدد اول دوددهای عدد در عدد دوم نتیجه
1 1 1
1 0 0
0 1 0
0 0 0

مثلاً برای انجام عملیات AND بین 25 و 3، کد زیر را استفاده کنید:

var iResult = 25 & 3;
alert(iResult);	// خروجی "1"

نتیجه عملیات AND بین 25 و 3 برابر با 1 است. چرا؟ تحلیل زیر را ببینید:

 25 = 0000 0000 0000 0000 0000 0000 0001 1001
  3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
AND = 0000 0000 0000 0000 0000 0000 0000 0001

می‌توان دید که در 25 و 3، تنها یک شماره‌ای که در آن‌ها یک در یک شماره‌ای (شماره 0) وجود دارد، وجود دارد، بنابراین، شماره‌های دیگر به 0 تبدیل می‌شوند و نتیجه 1 است.

عملیات بیتی OR

عملیات بیتی OR با نشانه (|) نشان داده می‌شود و مستقیماً روی فرم دودویی اعداد عمل می‌کند. در محاسبه هر عدد، عملگر OR از قوانین زیر استفاده می‌کند:

دوددهای عدد در عدد اول دوددهای عدد در عدد دوم نتیجه
1 1 1
1 0 1
0 1 1
0 0 0

به عنوان مثال، برای انجام عملیات OR بین 25 و 3 با استفاده از عملگر AND، کد زیر را استفاده کنید:

var iResult = 25 | 3;
alert(iResult);	// خروجی "27"

نتیجه عملیات OR بین 25 و 3 برابر با 27 است:

25 = 0000 0000 0000 0000 0000 0000 0001 1001
 3 = 0000 0000 0000 0000 0000 0000 0000 0011
--------------------------------------------
OR = 0000 0000 0000 0000 0000 0000 0001 1011

می‌توان دید که در دو عدد، 4 شماره‌ای که حاوی 1 هستند، به نتیجه منتقل می‌شوند. کد دودویی 11011 برابر با 27 است.

آپراکریتور دوددهی XOR

آپراکریتور دوددهی XOR با نشانه‌ی (^) نشان داده می‌شود، البته، مستقیماً بر روی فرم باینری عمل می‌کند. XOR با OR متفاوت است، تنها زمانی که یک دوددها با 1 ذخیره شده باشد، نتیجه‌ی آن 1 است. جدول حقیقت如下:

دوددهای عدد در عدد اول دوددهای عدد در عدد دوم نتیجه
1 1 0
1 0 1
0 1 1
0 0 0

آپراکریتور XOR بین 25 و 3 به صورت زیر است:

var iResult = 25 ^ 3;
alert(iResult);	// نمایش "26"

نتیجه‌ی آپراکریتور XOR بین 25 و 3 برابر با 26 است:

 25 = 0000 0000 0000 0000 0000 0000 0001 1001
  3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
XOR = 0000 0000 0000 0000 0000 0000 0001 1010

می‌توان دید که در دو عدد، 4 تا دوددها با 1 ذخیره شده‌اند که این دوددها به نتیجه‌ی نهایی منتقل می‌شوند. کد باینری 11010 برابر با 26 است.

حرکت به چپ

حرکت به چپ با دو برج کوچک نشان داده می‌شود (<<). این آپراکریتور تمامی دوددهای عدد را به سمت چپ حرکت می‌دهد که تعداد مشخصی از دوددها را مشخص می‌کند. به عنوان مثال، اگر عدد 2 (برابر با باینری 10) را 5 تا به چپ حرکت دهیم، نتیجه‌ی آن 64 (برابر با باینری 1000000) است:

var iOld = 2;		// برابر با باینری 10
var iNew = iOld << 5;	// برابر با باینری 1000000 و دسیمال 64

توجه:در حرکت به چپ دوددهای عدد، 5 تا دوددهای خالی به سمت چپ می‌آید. حرکت به چپ با استفاده از 0 این دوددهای خالی را پر می‌کند تا نتیجه‌ی کامل 32 بیتی باشد.

اعداد 2 با عملیات چپ‌رفت

توجه:حرکت به چپ نشانه‌ی دوددهای عدد را حفظ می‌کند. به عنوان مثال، اگر -2 را 5 تا به چپ حرکت دهیم، به -64 تبدیل می‌شود، نه 64. "آیا نشانه‌ی دوددها همچنان در دوددهای 32ام ذخیره می‌شود؟" بله، اما این کار در پس‌زمینه‌ی ECMAScript انجام می‌شود و توسعه‌دهندگان نمی‌توانند مستقیماً به دوددهای 32ام دسترسی پیدا کنند. حتی اگر عدد منفی را به صورت رشته‌ی باینری نمایش دهیم، نشان داده شده نیز نشانه‌ی منفی است (مثلاً -2 به صورت -10 نمایش داده می‌شود).

آپراکریتور حرکت به راست

آپراکریتور حرکت به راست با دو برج بزرگ نشان داده می‌شود (>>). این آپراکریتور تمامی دوددهای یک عدد 32 بیتی را به طور کامل به سمت راست حرکت می‌دهد و در عین حال نشانه‌ی عدد را (مثبت یا منفی) حفظ می‌کند. آپراکریتور حرکت به راست دقیقاً برعکس حرکت به چپ است. به عنوان مثال، اگر 64 را 5 تا به سمت راست حرکت دهیم، به 2 تبدیل می‌شود:

var iOld = 64;		// برابر با باینری 1000000
var iNew = iOld >> 5;	// برابر با باینری 10 دهی 2

همانطور که قبلاً ذکر شد، حرکت بیت‌ها باعث ایجاد فضاهای خالی می‌شود. این بار، فضاهای خالی در سمت چپ عدد قرار دارند، اما پس از نشانه‌ی علامت. ECMAScript از ارزش نشانه‌ی علامت برای پر کردن این فضاهای خالی استفاده می‌کند تا یک عدد کامل ایجاد کند، مانند تصویر زیر:

اعداد 64 با عملیات چپ‌رفت مثبت

حرکت به سمت راست بی‌علامت

عملگر حرکت به سمت راست بی‌علامت با سه علامت بزرگتر (>>>) نشان داده می‌شود، که تمام بیت‌های یک عدد بی‌علامت 32‌بیت را به سمت راست حرکت می‌دهد. برای اعداد مثبت، نتیجه حرکت به سمت راست بی‌علامت مشابه حرکت به سمت راست علامت‌دار است.

با استفاده از مثال حرکت به سمت راست علامت‌دار، اگر 64 را 5 بیت به سمت راست حرکت دهیم، 2 خواهد شد:

var iOld = 64;		// برابر با باینری 1000000
var iNew = iOld >>> 5;	// برابر با باینری 10 دهی 2

برای اعداد منفی، وضعیت متفاوت است.

حرکت به سمت راست بی‌علامت با پر کردن تمام فضاهای خالی با صفر انجام می‌شود. برای اعداد مثبت، این عمل مشابه عمل حرکت به سمت راست علامت‌دار است، اما برای اعداد منفی، اعداد منفی به عنوان اعداد مثبت در نظر گرفته می‌شوند.

نتایج حرکت به سمت راست بی‌علامت یک عدد 32‌بیت مثبت است، بنابراین حرکت به سمت راست بی‌علامت یک عدد منفی همیشه یک عدد بسیار بزرگ را تولید می‌کند. به عنوان مثال، اگر -64 را 5 بیت به سمت راست حرکت دهیم، 134217726 را دریافت می‌کنیم. چگونه این نتیجه به دست می‌آید؟

برای انجام این کار، نیاز به تبدیل این عدد به شکل بی‌علامت مشابه داریم (با اینکه خود عدد هنوز علامت‌دار است)، که می‌توانیم این شکل را با کد زیر دریافت کنیم:

var iUnsigned64 = -64 >>> 0;

سپس، با استفاده از toString() نوع Number برای دریافت نمونه واقعی بیت‌ها، با استفاده از پایه 2:

alert(iUnsigned64.toString(2));

این عدد 11111111111111111111111111000000 را تولید می‌کند، که باینری علامت‌دار -64 است، اما برابر با عدد صحیح بی‌علامت 4294967232 است.

به همین دلیل، باید از عملگر حرکت به سمت راست بدون سیگنال بدون دقت کرد.