This commit is contained in:
Ilya Kantor 2014-10-26 22:10:13 +03:00
parent 06f61d8ce8
commit f301cb744d
2271 changed files with 103162 additions and 0 deletions

View file

@ -0,0 +1,6 @@
<ol>
<li>Операция `a^b` ставит бит результата в `1`, если на соответствующей битовой позиции в `a` или `b` (но не одновременно) стоит `1`.
Так как в `0` везде стоят нули, то биты берутся в точности как во втором аргументе.</li>
<li>Первое побитовое НЕ `~` превращает `0` в `1`, а `1` в `0`. А второе НЕ превращает ещё раз, в итоге получается как было.</li>
</ol>

View file

@ -0,0 +1,13 @@
# Побитовый оператор и значение
[importance 5]
Почему побитовые операции в примерах ниже не меняют число? Что они делают внутри?
```js
//+ run
alert( 123 ^ 0 ); // 123
alert( 0 ^ 123 ); // 123
alert( ~~123 ); // 123
```

View file

@ -0,0 +1,14 @@
Один из вариантов такой функции:
```js
//+ run
function isInteger(num) {
return (num ^ 0) === num;
}
alert( isInteger(1) ); // true
alert( isInteger(1.5) ); // false
alert( isInteger(-0.5) ); // false
```
Обратите внимание: `num^0` -- в скобках! Это потому, что приоритет операции `^` очень низкий. Если не поставить скобку, то `===` сработает раньше. Получится `num ^ (0 === num)`, а это уже совсем другое дело.

View file

@ -0,0 +1,14 @@
# Проверка, целое ли число
[importance 3]
Напишите функцию `isInteger(num)`, которая возвращает `true`, если `num` -- целое число, иначе `false`.
Например:
```js
alert( isInteger(1) ); // true
alert( isInteger(1.5) ); // false
alert( isInteger(-0.5) ); // false
```

View file

@ -0,0 +1,23 @@
Операция над числами, в конечном итоге, сводится к битам.
Посмотрим, можно ли поменять местами биты слева и справа.
Например, таблица истинности для `^`:
<table class="bordered">
<tr>
<th>`a`</th>
<th>`b`</th>
<th>результат</th>
</tr>
<tr><td>`0`</td><td>`0`</td><td>`0`</td></tr>
<tr><td>`0`</td><td>`1`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`0`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`1`</td><td>`0`</td></tr>
</table>
Случаи `0^0` и `1^1` заведомо не изменятся при перемене мест, поэтому нас не интересуют. А вот `0^1` и `1^0` эквивалентны и равны `1`.
Аналогично можно увидеть, что и другие операторы симметричны.
Ответ: **да**.

View file

@ -0,0 +1,12 @@
# Симметричны ли операции ^, |, &?
[importance 5]
Верно ли, что для любых `a` и `b` выполняются равенства ниже?
<ul>
<li>`a ^ b == b ^ a`</li>
<li>`a & b == b & a`</li>
<li>`a | b == b | a`</li>
</ul>
Иными словами, при перемене мест -- всегда ли результат остаётся тем же?

View file

@ -0,0 +1,29 @@
Всё дело в том, что побитовые операции преобразуют число в 32-битное целое.
Обычно число в JavaScript имеет 64-битный формат с плавающей точкой. При этом часть битов (`52`) отведены под цифры, часть (`11`) отведены под хранение номера позиции, на которой стоит десятичная точка, и один бит -- знак числа.
Это означает, что максимальное целое число, которое можно хранить, занимает `52` бита.
Число `12345678912345` в двоичном виде: `10110011101001110011110011100101101101011001` (44 цифры).
Побитовый оператор `^` преобразует его в 32-битное путём отбрасывания десятичной точки и "лишних" старших цифр. При этом, так как число большое и старшие биты здесь ненулевые, то, естественно, оно изменится.
Вот ещё пример:
```js
//+ run
// в двоичном виде 1000000000000000000000000000000 (31 цифры)
alert( Math.pow(2, 30) ); // 1073741824
alert( Math.pow(2, 30) ^ 0 ); // 1073741824, всё ок, длины хватает
// в двоичном виде 100000000000000000000000000000000 (33 цифры)
alert( Math.pow(2, 32) ); // 4294967296
alert( Math.pow(2, 32) ^ 0 ); // 0, отброшены старшие цифры, остались нули
// пограничный случай
// в двоичном виде 10000000000000000000000000000000 (32 цифры)
alert( Math.pow(2, 31) ); // 2147483648
alert( Math.pow(2, 31) ^ 0 ); // -2147483648, ничего не отброшено,
// но первый бит 1 теперь стоит в начале числа и является знаковым
```

View file

@ -0,0 +1,12 @@
# Почему результат разный?
[importance 5]
Почему результат второго `alert'а` такой странный?
```js
//+ run
alert( 123456789 ^ 0 ); // 123456789
alert( 12345678912345 ^ 0 ); // 1942903641
```

View file

@ -0,0 +1,752 @@
# Побитовые операторы
Побитовые операторы интерпретируют операнды как последовательность из 32 битов (нулей и единиц). Они производят операции, используя двоичное представление числа, и возвращают новую последовательность из 32 бит (число) в качестве результата.
**Эта глава сложная, требует дополнительных знаний в программировании и не очень важная, вы можете пропустить её.**
[cut]
## Формат 32-битного целого числа со знаком [#signed-format]
Побитовые операторы в JavaScript работают с 32-битными целыми числами в их двоичном представлении.
Это представление называется "32-битное целое со знаком, старшим битом слева и дополнением до двойки".
Разберём, как устроены числа внутри подробнее, это необходимо знать для битовых операций с ними.
<ul>
<li>Что такое [двоичная система счисления](http://ru.wikipedia.org/wiki/%C4%E2%EE%E8%F7%ED%E0%FF_%F1%E8%F1%F2%E5%EC%E0_%F1%F7%E8%F1%EB%E5%ED%E8%FF), вам, надеюсь, уже известно. При разборе побитовых операций мы будем обсуждать именно двоичное представление чисел, из 32 бит.
</li>
<li>*Старший бит слева* -- это научное название для самого обычного порядка записи цифр (от большего разряда к меньшему). При этом, если больший разряд отсутствует, то соответствующий бит равен нулю.
Примеры представления чисел в двоичной системе:
```js
a = 0; // 00000000000000000000000000000000
a = 1; // 00000000000000000000000000000001
a = 2; // 00000000000000000000000000000010
a = 3; // 00000000000000000000000000000011
a = 255;// 00000000000000000000000011111111
```
Обратите внимание, каждое число состоит ровно из 32-битов.
[smart header="Младший бит слева"]
Несмотря на то, что нам такой способ записи чисел кажется не совсем обычным, бывают языки и технологии, использующие способ записи "младший бит слева", когда биты пишутся наоборот, от меньшего разряда к большему.
Именно поэтому спецификация EcmaScript явно говорит "старший бит слева".
[/smart]
</li>
<li>*Дополнение до двойки* -- это название способа поддержки отрицательных чисел.
**Двоичный вид числа, обратного данному (например, `5` и `-5`) получается путём обращения всех битов с прибавлением 1.**
То есть, нули заменяются на единицы, единицы -- на нули и к числу прибавляется `1`. Получается внутреннее представление того же числа, но со знаком минус.
Например, вот число `314`:
```js
00000000000000000000000100111010
```
Чтобы получить `-314`, первый шаг -- обратить биты числа: заменить `0` на `1`, а `1` на `0`:
```js
11111111111111111111111011000101
```
Второй шаг -- к полученному двоичному числу приплюсовать единицу, обычным двоичным сложением: `11111111111111111111111011000101 + 1 = 11111111111111111111111011000110`.
Итак, мы получили:
```js
-314 = 11111111111111111111111011000110
```
Принцип дополнения до двойки делит все двоичные представления на два множества: если крайний-левый бит равен `0` -- число положительное, если `1` -- число отрицательное. Поэтому этот бит называется <i>знаковым битом</i>.
</li>
</ul>
## Список операторов
В следующей таблице перечислены все побитовые операторы.
Далее операторы разобраны более подробно.
<table class="fullwidth-table">
<tr>
<th>Оператор</th>
<th>Использование</th>
<th>Описание</th>
</tr>
<tr>
<td>Побитовое И (AND)</td>
<td style="white-space: nowrap"><code>a &amp; b</code></td>
<td>Ставит 1 на бит результата, для которого соответствующие биты операндов равны 1.</td>
</tr>
<tr>
<td>Побитовое ИЛИ (OR)</td>
<td style="white-space: nowrap"><code>a | b</code></td>
<td>Ставит 1 на бит результата, для которого хотя бы один из соответствующих битов операндов равен 1.</td>
</tr>
<tr>
<td>Побитовое исключающее ИЛИ (XOR)</td>
<td style="white-space: nowrap"><code>a ^ b</code></td>
<td>Ставит 1 на бит результата, для которого только один из соответствующих битов операндов равен 1 (но не оба).</td>
</tr>
<tr>
<td>Побитовое НЕ (NOT)</td>
<td style="white-space: nowrap"><code>~a</code></td>
<td>Заменяет каждый бит операнда на противоположный.</td>
</tr>
<tr>
<td>Левый сдвиг</td>
<td style="white-space: nowrap">`a << b`</td>
<td>Сдвигает двоичное представление <code>a</code> на <code>b</code> битов влево, добавляя справа нули.</td>
</tr>
<tr>
<td>Правый сдвиг, переносящий знак</td>
<td style="white-space: nowrap">`a >> b`</td>
<td>Сдвигает двоичное представление <code>a</code> на <code>b</code> битов вправо, отбрасывая сдвигаемые биты.</td>
</tr>
<tr>
<td>Правый сдвиг с заполнением нулями</td>
<td style="white-space: nowrap">`a >>> b`</td>
<td>Сдвигает двоичное представление <code>a</code> на <code>b</code> битов вправо, отбрасывая сдвигаемые биты и добавляя нули слева.</td>
</tr>
</table>
## Описание работы операторов
Побитовые операторы работают следующим образом:
<ol>
<li>Операнды преобразуются в 32-битные целые числа, представленные последовательностью битов. Дробная часть, если она есть, отбрасывается.</li>
<li>Для бинарных операторов -- каждый бит в первом операнде рассматривается вместе с соответствующим битом второго операнда: первый бит с первым, второй со вторым и т.п. Оператор применяется к каждой паре бит, давая соответствующий бит результата.</li>
<li>Получившаяся в результате последовательность бит интерпретируется как обычное число.</li>
</ol>
Посмотрим, как работают операторы, на примерах.
### & (Побитовое И)
Выполняет операцию И над каждой парой бит.
Результат `a & b` равен единице только когда оба бита `a` и `b` равны единице.
Таблица истинности для `&`:
<table class="standard-table">
<tr><th>`a`</th><th>`b`</th><th>`a & b`</th></tr>
<tr><td>`0`</td><td>`0`</td><td>`0`</td></tr>
<tr><td>`0`</td><td>`1`</td><td>`0`</td></tr>
<tr><td>`1`</td><td>`0`</td><td>`0`</td></tr>
<tr><td>`1`</td><td>`1`</td><td>`1`</td></tr>
</table>
Пример:
```js
9 (по осн. 10)
= 00000000000000000000000000001001 (по осн. 2)
14 (по осн. 10)
= 00000000000000000000000000001110 (по осн. 2)
--------------------------------
14 & 9 (по осн. 10)
= 00000000000000000000000000001000 (по осн. 2)
= 8 (по осн. 10)
```
### | (Побитовое ИЛИ)
Выполняет операцию ИЛИ над каждой парой бит. Результат `a | b` равен 1, если хотя бы один бит из <code>a,b</code> равен 1.
Таблица истинности для `|`:
<table class="standard-table">
<tr><th>`a`</th><th>`b`</th><th>`a | b`</th></tr>
<tr><td>`0`</td><td>`0`</td><td>`0`</td></tr>
<tr><td>`0`</td><td>`1`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`0`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`1`</td><td>`1`</td></tr>
</table>
Пример:
```js
9 (по осн. 10)
= 00000000000000000000000000001001 (по осн. 2)
14 (по осн. 10)
= 00000000000000000000000000001110 (по осн. 2)
--------------------------------
14 | 9 (по осн. 10)
= 00000000000000000000000000001111 (по осн. 2)
= 15 (по осн. 10)
```
### ^ (Исключающее ИЛИ)
Выполняет операцию "Исключающее ИЛИ" над каждой парой бит.
<code>a</code> Исключающее ИЛИ <code>b</code> равно 1, если только <code>a=1</code> или только <code>b=1</code>, но не оба одновременно <code>a=b=1</code>.
Таблица истинности для исключающего ИЛИ:
<table class="standard-table">
<tr><th>`a`</th><th>`b`</th><th>`a ^ b`</th></tr>
<tr><td>`0`</td><td>`0`</td><td>`0`</td></tr>
<tr><td>`0`</td><td>`1`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`0`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`1`</td><td>`0`</td></tr>
</table>
Как видно, оно даёт 1, если ЛИБО слева `1`, ЛИБО справа `1`, но не одновременно. Поэтому его и называют "исключающее ИЛИ".
Пример:
```js
9 (по осн. 10)
= 00000000000000000000000000001001 (по осн. 2)
14 (по осн. 10)
= 00000000000000000000000000001110 (по осн. 2)
--------------------------------
14 ^ 9 (по осн. 10)
= 00000000000000000000000000000111 (по осн. 2)
= 7 (по осн. 10)
```
[smart header="Исключающее ИЛИ в шифровании"]
Исключающее или можно использовать для шифрования, так как эта операция полностью обратима. Двойное применение исключающего ИЛИ с тем же аргументом даёт исходное число.
Иначе говоря, верна формула: `a ^ b ^ b == a`.
Пускай Вася хочет передать Пете секретную информацию `data`. Эта информация заранее превращена в число, например строка интерпретируется как последовательность кодов символов.
Вася и Петя заранее договариваются о числовом ключе шифрования `key`.
Алгоритм:
<ul>
<li>Вася берёт двоичное представление `data` и делает операцию `data ^ key`. При необходимости `data` бьётся на части, равные по длине `key`, чтобы можно было провести побитовое ИЛИ `^` для каждой части. В JavaScript оператор `^` работает с 32-битными целыми числами, так что `data` нужно разбить на последовательность таких чисел.</li>
<li>Результат `data ^ key` отправляется Пете, это шифровка.</li>
</ul>
Например, пусть в `data` очередное число равно `9`, а ключ `key` равен `1220461917`.
```js
Данные: 9 в двоичном виде
00000000000000000000000000001001
Ключ: 1220461917 в двоичном виде
01001000101111101100010101011101
Результат операции 9 ^ key:
01001000101111101100010101010100
Результат в 10-ной системе (шифровка):
1220461908
```
<ul>
<li>Петя, получив очередное число шифровки `1220461908`, применяет к нему такую же операцию `^ key`.</li>
<li>Результатом будет исходное число `data`.</li>
</ul>
В нашем случае:
```js
Полученная шифровка в двоичной системе:
9 ^ key = 1220461908
01001000101111101100010101010100
Ключ: 1220461917 в двоичном виде:
01001000101111101100010101011101
Результат операции 1220461917 ^ key:
00000000000000000000000000001001
Результат в 10-ной системе (исходное сообщение):
9
```
Конечно, такое шифрование поддаётся частотному анализу и другим методам дешифровки, поэтому современные алгоритмы используют операцию XOR `^` как одну из важных частей более сложной многоступенчатой схемы.
[/smart]
### ~ (Побитовое НЕ)
Производит операцию НЕ над каждым битом, заменяя его на обратный ему.
Таблица истинности для НЕ:
<table class="standard-table">
<tr><th>`a`</th><th>`~a`</th></tr>
<tr><td>`0`</td><td>`1`</td></tr>
<tr><td>`1`</td><td>`0`</td></tr>
</table>
Пример:
```js
9 (по осн. 10)
= 00000000000000000000000000001001 (по осн. 2)
--------------------------------
~9 (по осн. 10)
= 11111111111111111111111111110110 (по осн. 2)
= -10 (по осн. 10)
```
Из-за внутреннего представления отрицательных чисел получается так, что `~n == -(n+1)`.
Например:
```js
//+ run
alert(~3); // -4
alert(~-1); // 0
```
### << (Битовый сдвиг влево)
Операторы битового сдвига принимают два операнда. Первый -- это число для сдвига, а второй -- количество битов, которые нужно сдвинуть в первом операнде.
Оператор `<<` сдвигает первый операнд на указанное число битов влево. Лишние биты отбрасываются, справа добавляются нулевые биты.
Например, `9 << 2` даст `36`:
```js
9 (по осн.10)
= 00000000000000000000000000001001 (по осн.2)
--------------------------------
9 << 2 (по осн.10)
= 00000000000000000000000000100100 (по осн.2)
= 36 (по осн.10)
```
Операция `<< 2` сдвинула и отбросила два левых нулевых бита и добавила справа два новых нулевых.
[smart header="Левый сдвиг почти равен умножению на 2"]
Битовый сдвиг `<< N` обычно имеет тот же эффект, что и умножение на два `N` раз, например:
```js
//+ run
alert( 3 << 1 ); // 6, умножение на 2
alert( 3 << 2 ); // 12, умножение на 2 два раза
alert( 3 << 3 ); // 24, умножение на 2 три раза
```
Конечно, следует иметь в виду, что побитовые операторы работают только с 32-битными числами, поэтому верхний порог такого "умножения" ограничен:
```js
//+ run
*!*
alert(10000000000 << 1); // -1474836480, отброшен крайний-левый бит
*/!*
alert(10000000000 * 2); // 20000000000, обычное умножение
```
[/smart]
### >> (Правый битовый сдвиг, переносящий знак)
Этот оператор сдвигает биты вправо, отбрасывая лишние. При этом слева добавляется *копия* крайнего-левого бита.
Знак числа (представленный крайним-левым битом) при этом не меняется, так как новый крайний-левый бит имеет то же значение, что и исходном числе.
Поэтому он назван "переносящим знак".
Например, `9 >> 2` даст <code>2</code>:
```js
9 (по осн.10)
= 00000000000000000000000000001001 (по осн.2)
--------------------------------
9 >> 2 (по осн.10)
= 00000000000000000000000000000010 (по осн.2)
= 2 (по осн.10)
```
Операция `>> 2` сдвинула вправо и отбросила два правых бита `01` и добавила слева две копии первого бита `00`.
Аналогично, `-9 >> 2` даст `-3`:
```js
-9 (по осн.10)
= 11111111111111111111111111110111 (по осн.2)
--------------------------------
-9 >> 2 (по осн.10)
= 11111111111111111111111111111101 (по осн.2) = -3 (по осн.10)
```
Здесь операция `>> 2` сдвинула вправо и отбросила два правых бита `11` и добавила слева две копии первого бита `11`. , Знак числа сохранён, так как крайний-левый (знаковый) бит сохранил значение `1`.
[smart header="Правый сдвиг почти равен целочисленному делению на 2"]
Битовый сдвиг `>> N` обычно имеет тот же, что и целочисленное деление на два `N` раз:
```js
//+ run
alert( 100 >> 1 ); // 50, деление на 2
alert( 100 >> 2 ); // 25, деление на 2 два раза
alert( 100 >> 3 ); // 12, деление на 2 три раза, целая часть от результата
```
[/smart]
### >>> (Правый сдвиг с заполнением нулями)
Этот оператор сдвигает биты первого операнда вправо. Лишние биты справа отбрасываются. Слева добавляются нулевые биты.
Знаковый бит становится равным 0, поэтому результат всегда положителен.
**Для неотрицательных чисел правый сдвиг с заполнением нулями `>>>` и правый сдвиг с переносом знака `>>` дадут одинаковый результат, т.к в обоих случаях слева добавятся нули.**
Для отрицательных чисел -- результат работы разный. Например, `-9 >>> 2` даст `1073741821`, в отличие от `-9 >> 2` (дает `-3`):
```js
-9 (по осн.10)
= 11111111111111111111111111110111 (по осн.2)
--------------------------------
-9 >>> 2 (по осн.10)
= 00111111111111111111111111111101 (по осн.2)
= 1073741821 (по осн.10)
```
## Применение побитовых операторов
Побитовые операторы используются редко, но всё же используются.
Случаи применения побитовых операторов, которые мы здесь разберём, составляют большинство всех использований в JavaScript.
[warn header="Осторожно, приоритеты!"]
В JavaScript побитовые операторы `^`, `&`, `|` выполняются после сравнений `==`.
Например, в сравнении `a == b^0` будет сначала выполнено сравнение `a == b`, а потом уже операция `^0`, как будто стоят скобки `(a == b)^0`.
Обычно это не то, чего мы хотим. Чтобы гарантировать желаемый порядок, нужно ставить скобки: `a == (b^0)`.
[/warn]
### Маска
Для этого примера представим, что наш скрипт работает с пользователями:
<ul>
<li>`Гость`</li>
<li>`Редактор`</li>
<li>`Админ`</li>
</ul>
У каждого из них есть ряд доступов, которые можно свести в таблицу:
<table>
<tr>
<th>Пользователь</th>
<th>Просмотр статей</th>
<th>Изменение статей</th>
<th>Просмотр товаров</th>
<th>Изменение товаров</th>
<th>Управление правами</th>
</tr>
<tr>
<td>Гость</td>
<td>Да</td>
<td>Нет</td>
<td>Да</td>
<td>Нет</td>
<td>Нет</td>
</tr>
<tr>
<td>Редактор</td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
<td>Нет</td>
</tr>
<tr>
<td>Админ</td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
</tr>
</table>
Если вместо "Да" поставить `1`, а вместо "Нет" -- `0`, то каждый набор доступов описывается числом:
<table>
<tr>
<th>Пользователь</th>
<th>Просмотр статей</th>
<th>Изменение статей</th>
<th>Просмотр товаров</th>
<th>Изменение товаров</th>
<th>Управление правами</th>
<th>В 10-ной системе</th>
</tr>
<tr>
<td>Гость</td>
<td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td> = 20</td>
</tr>
<tr>
<td>Редактор</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>0</td>
<td> = 30</td>
</tr>
<tr>
<td>Админ</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td> = 31</td>
</tr>
</table>
**Мы "упаковали" много информации в одно число. Это экономит память. Но, кроме этого, по нему очень легко проверить, имеет ли посетитель заданную *комбинацию доступов*!**
Для этого посмотрим, как в 2-ной системе представляется каждый доступ в отдельности.
<ul>
<li>Доступ, соответствующий только управлению правами: `00001 (=1)` (все нули кроме `1` на позиции, соответствующей этому доступу).</li>
<li>Доступ, соответствующий только изменению товаров: `00010 (=2)`.</li>
<li>Доступ, соответствующий только просмотру товаров: `00100 (=4)`.</li>
<li>Доступ, соответствующий только изменению статей: `01000 (=8)`.</li>
<li>Доступ, соответствующий только просмотру статей: `10000 (=16)`.</li>
</ul>
Например, просматривать и изменять статьи позволит доступ `access = 11000`.
Как правило, доступы задаются в виде констант:
```js
var ACCESS_ADMIN = 1; // 00001
var ACCESS_GOODS_CHANGE = 2; // 00010
var ACCESS_GOODS_VIEW = 4; // 00100
var ACCESS_ARTICLE_CHANGE = 8; // 01000
var ACCESS_ARTICLE_VIEW = 16; // 10000
```
Из этих констант получить нужную комбинацию доступов можно при помощи операции `|`.
```js
var access = ACCESS_ARTICLE_VIEW | ACCESS_ARTICLE_CHANGE; // 11000
```
### Двоичные числа в JavaScript
Для удобной работы с примерами в этой статье пригодятся две функции.
<ul>
<li>`parseInt("11000", 2)` -- переводит строку с двоичной записью числа в число.</li>
<li>`n.toString(2)` -- получает для числа `n` запись в 2-ной системе в виде строки.</li>
</ul>
Например:
```js
//+ run
var access = parseInt("11000", 2); // получаем число из строки
alert(access); // 24, число с таким 2-ным представлением
var access2 = access.toString(2); // обратно двоичную строку из числа
alert(access2); // 11000
```
### Проверка доступов
**Для того, чтобы понять, есть ли в доступе `access` нужный доступ, например управление правами -- достаточно применить к нему побитовый оператор И (`&`) с соответствующей маской.**
Создадим для примера ряд доступов и проверим их:
```js
//+ run
var access = parseInt("11111", 2); // 31, все 1 означает, что доступ полный
alert(access & ACCESS_ADMIN); // если результат не 0, то есть доступ ACCESS_ADMIN
```
А теперь та же проверка для посетителя с другими правами:
```js
//+ run
var access = parseInt("10100"); // 20, нет 1 в конце
alert(access & ACCESS_ADMIN); // 0, нет доступа к управлению правами
```
Такая проверка работает, потому что оператор И ставит `1` на те позиции результата, на которых в обоих операндах стоит `1`.
Так что `access & 1` для любого числа `access` поставит все биты в ноль, кроме самого правого. А самый правый станет `1` только если он равен `1` в `access`.
Для полноты картины также проверим, даёт ли доступ `11111` право на изменение товаров. Для этого нужно применить к доступу оператор И (`&`) с `00010` (=`2` в 10-ной системе).
```js
//+ run
var adminAccess = 31; // 111*!*1*/!*1
alert(adminAccess & ACCESS_GOODS_CHANGE); // не 0, есть доступ к изменению товаров
```
**Можно проверить один из нескольких доступов.**
Например, проверим, есть ли права на просмотр ИЛИ изменение товаров. Соответствующие права задаются битом `1` на втором и третьем месте с конца, что даёт число `00110` (=`6` в 10-ной системе).
```js
//+ run
var check = ACCESS_GOODS_VIEW | ACCESS_GOODS_CHANGE; // 6, 00110
var access = 30; // 11*!*11*/!*0;
alert(access & check); // не 0, значит есть доступ к просмотру ИЛИ изменению
access = parseInt("11100", 2);
alert(access & check); // не 0, есть доступ к просмотру ИЛИ изменению
```
Как видно из примера выше, если в аргументе `check` стоит ИЛИ из нескольких доступов `ACCESS_*`, то и результат проверки скажет, есть ли хотя бы один из них. А какой -- нужно смотреть отдельной проверкой, если это важно.
**Итак, маска даёт возможность удобно "паковать" много битовых значений в одно число при помощи ИЛИ `|`, а также, при помощи оператора И (`&`), проверять маску на комбинацию установленных битов.**
### Маски в функциях
Зачастую маски используют в функциях, чтобы одним параметром передать несколько "флагов", т.е. однобитных значений.
Например:
```js
// найти пользователей с правами на изменение товаров или администраторов
findUsers(ACCESS_GOODS_CHANGE | ACCESS_ADMIN);
```
### Округление
**Так как битовые операции отбрасывают десятичную часть, то их можно использовать для округления. Достаточно взять любую операцию, которая не меняет значение числа.**
Например, двойное НЕ (`~`):
```js
//+ run
alert( ~~12.345 ); // 12
```
Подойдёт и Исключающее ИЛИ (`^`) с нулём:
```js
//+ run
alert( 12.345^0 ); // 12
```
Последнее даже более удобно, поскольку отлично читается:
```js
//+ run
alert( 12.3 * 14.5 ^ 0); // (=178) "12.3 умножить на 14.5 *!*и округлить*/!*"
```
У побитовых операторов достаточно низкий приоритет, он меньше чем у остальной арифметики:
```js
//+ run
alert( 1.1 + 1.2 ^ 0 ); // 2, сложение выполнится раньше округления
```
### Проверка на -1
[Внутренний формат](#signed-format) чисел устроен так, что для смены знака нужно все биты заменить на противоположные ("обратить") и прибавить `1`.
Обращение битов -- это побитовое НЕ (`~`). То есть, при таком формате представления числа `-n = ~n + 1`. Или, если перенести единицу: `~n = -(n+1)`.
Как видно из последнего равенства, `~n == 0` только если `n == -1`. Поэтому можно легко проверить равенство `n == -1`:
```js
//+ run
var n = 5;
if (~n) { // сработает, т.к. ~n = -(5+1) = -6
alert("n не -1"); // выведет!
}
```
```js
//+ run
var n = -1;
if (~n) { // не сработает, т.к. ~n = -(-1+1) = 0
alert("...ничего не выведет...");
}
```
Проверка на `-1` пригождается, например, при поиске символа в строке. Вызов `str.indexOf("подстрока")` возвращает позицию подстроки в `str`, или `-1` если не нашёл.
```js
//+ run
var str = "Проверка";
if (~str.indexOf("верка")) { // Сочетание "if (~...indexOf)" читается как "если найдено"
alert('найдено!');
}
```
### Умножение и деление на степени 2
**Оператор `a << b`, сдвигая биты, по сути умножает `a` на <code>2<sup>b</sup></code>.**
Например:
```js
//+ run
alert( 1 << 2 ); // 1*(2*2) = 4
alert( 1 << 3 ); // 1*(2*2*2) = 8
alert( 3 << 3 ); // 3*(2*2*2) = 24
```
При этом следует иметь в виду, что максимальный верхний порог такого умножения меньше, чем обычно, так как побитовый оператор оперирует 32-битными целыми, в то время как обычные операторы оперируют числами длиной 64 бита.
**Оператор `a >> b`, сдвигая биты, производит целочисленное деление `a` на <code>2<sup>b</sup></code>.**
```js
//+ run
alert( 8 >> 2 ); // 2 = 8/4, убрали 2 нуля в двоичном представлении
alert( 11 >> 2 ); // 2, целочисленное деление (менее значимые биты просто отброшены)
```
## Итого
<ul>
<li>Бинарные побитовые операторы: `& | ^ << >> >>>`.</li>
<li>Унарный побитовый оператор один: `~`.</li>
</ul>
Как правило, битовое представление числа используется для:
<ul>
<li>Упаковки нескольких битововых значений ("флагов") в одно значение. Это экономит память и позволяет проверять наличие комбинации флагов одним оператором `&`. Кроме того, такое упакованное значение будет для функции всего одним параметром, это тоже удобно.</li>
<li>Округления числа: `(12.34^0) = 12`.</li>
<li>Проверки на равенство `-1`: `if (~n) { n не -1 }`.</li>
</ul>
[head]
<script>
var ACCESS_ADMIN = 1; // 00001
var ACCESS_GOODS_CHANGE = 2; // 00010
var ACCESS_GOODS_VIEW = 4; // 00100
var ACCESS_ARTICLE_CHANGE = 8; // 01000
var ACCESS_ARTICLE_VIEW = 16; // 10000
</script>
[/head]