Битовые операторы ECMAScript

Битовые операторы работают на уровне низового уровня чисел (то есть на 32 разрядах, представляющих число).

Пересмотрим целые числа

В ECMAScript есть два типа целых чисел: целые числа с знаками (разрешены как положительные, так и отрицательные числа) и целые числа без знака (разрешены только положительные числа). В ECMAScript все литералы целых чисел по умолчанию целые числа с знаками, что означает?

Двоичные числа с знаками используют 31 разряд для представления значения числа, 32-й разряд используется для представления знака числа, 0 означает положительное число, 1 означает отрицательное. Диапазон значений варьируется от -2147483648 до 2147483647.

Двоичные числа с знаками можно хранить двумя способами: один для хранения положительных чисел, другой для хранения отрицательных. Положительные числа хранятся в виде истинного двоичного формата, каждый разряд из 31 из которых代表着 степень 2, начиная с первого разряда (разряд 0),代表着 20Второй разряд (разряд 1) означает 21. Неиспользуемые разряды заполняются нулями, то есть они не учитываются. Например, на рисунке показано представление числа 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 к двоичному обратному коду

Чтобы определить двоичное представление -18, сначала необходимо получить двоичное представление 18, как показано ниже:

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-й бит не представляет знак числа, а равноценен значению 231Из-за этой дополнительной бита диапазон значений незнаковых целых чисел составляет от 0 до 4294967295. Для чисел, меньших 2147483647, незнаковые целые числа выглядят так же, как и знаковые целые числа, а для чисел, больших 2147483647, необходимо использовать бит 31 (в знаковых целых числах этот бит всегда равен 0).

Преобразование незнаковых целых чисел в строку возвращает только их эффективные биты.

Внимание:Все целочисленные литералы по умолчанию хранятся как знаковые целые числа. Только операторы битов ECMAScript могут создавать незнаковые целые числа.

Операции с битами NOT

Операции с битами NOT представляются символом отрицания (~), это один из немногих операторов, связанных с двоичной арифметикой в ECMAScript.

Битовое вычисление NOT является трехшаговым процессом:

  1. Преобразование операнда в 32-битное число
  2. Преобразование двоичного числа в его двоичное дополнение
  3. Преобразование двоичного числа в浮инговое число

Например:

var iNum1 = 25;		//25 равно 00000000000000000000000000011001
var iNum2 = ~iNum1;	//Преобразование в двоичное дополнение
alert(iNum2);		//вывод "-26"

Битовое вычисление NOT по сути является обратным числом, вычтенным из числа, и затем уменьшенным на 1, поэтому 25 становится -26. Получение такого же результата можно также получить следующим образом:

var iNum1 = 25;
var iNum2 = -iNum1 -1;
alert(iNum2);	//вывод -26

Битовое вычисление AND

Битовое вычисление 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) содержит 1, поэтому другие разряды генерируют 0, поэтому результат равен 1.

Битовое вычисление OR

Битовое вычисление OR обозначается символом (|) и напрямую выполняется над двоичным представлением чисел. При вычислении каждого разряда, оператор OR использует следующие правила:

Разряды первого числа Разряды второго числа Результат
1 1 1
1 0 1
0 1 1
0 0 0

Пример использования оператора AND, чтобы выполнить OR операцию 25 и 3, код如下所示:

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 пустых мест справа. Оператор левого сдвига заполняет эти места нулями, чтобы результат был полным 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.

Из-за этой причины использование оператора беззнакового правого сдвига требует осторожности.