Глава 3 · Выражения и операции
Побитовые операции и сдвиги
Флаги, маски и упакованные поля удобно хранить в отдельных разрядах целого числа. Побитовые операции читают и изменяют эти разряды у INTEGER и LONG: маской выделяют, устанавливают и очищают выбранные позиции, а сдвиги перемещают их влево или вправо в пределах ширины типа.
Нумерация разрядов и знак
INTEGER содержит 32 разряда, LONG содержит 64. Отсчёт начинается справа с нуля: разряд 0 соответствует единице, разряд 1 соответствует двум, разряд 2 соответствует четырём. Старший разряд, 31 или 63, определяет знак.
Отрицательные целые представлены дополнительным кодом, поэтому число -1 имеет единицы во всех разрядах своего типа. Побитовый результат также имеет целочисленный тип, а логические значения BOOLEAN к этим разрядам не приравниваются. Различия описаны в статье BOOLEAN, двоичная и шестнадцатеричная запись — в статье систем счисления.
Формы операций и тип результата
Каждая запись ниже обозначает отдельную целочисленную форму операции. В этих формах операнд должен иметь тип INTEGER или LONG:
NOT <целое выражение>
<левое целое> AND <правое целое>
<левое целое> OR <правое целое>
<левое целое> XOR <правое целое>
<левое целое> EQV <правое целое>
<левое целое> IMP <правое целое>
NOT сохраняет тип своего операнда. Бинарная операция над двумя INTEGER возвращает INTEGER, а если один операнд LONG, второй расширяется до LONG и результат получает тип LONG. При расширении отрицательного INTEGER значение сохраняется: добавленные старшие разряды заполняются единицами.
DOUBLE, DECIMAL, STRING и смесь BOOLEAN с целым числом в этих формах недопустимы. У NOT есть отдельная логическая форма с одним BOOLEAN, а у AND, OR, XOR, EQV и IMP — с двумя: она возвращает BOOLEAN, не обрабатывая числовые разряды, и её полный договор находится в статье BOOLEAN. ANDALSO и ORELSE принимают только BOOLEAN и побитового действия не имеют.
Правило для одной пары разрядов
NOT заменяет каждый ноль единицей, а каждую единицу нулём. Остальные операции применяют одно и то же правило к каждой паре соответствующих разрядов:
| A | B | AND | OR | XOR | EQV | IMP |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 1 | 1 |
| 0 | 1 | 0 | 1 | 1 | 0 | 1 |
| 1 | 0 | 0 | 1 | 1 | 0 | 0 |
| 1 | 1 | 1 | 1 | 0 | 1 | 1 |
AND оставляет единицу только при двух единицах, OR устанавливает её при хотя бы одной. XOR выбирает различающиеся разряды, EQV — совпадающие, IMP очищает разряд только при единице слева и нуле справа. Нули и единицы в таблице являются битами, а не литералами TRUE и FALSE.
NOT применяется ко всей ширине типа: NOT 0 равно -1, NOT -1 равно 0.
Выбор разрядов маской
Маска представляет выбранные позиции единицами: AND сохраняет в числе только эти позиции, OR устанавливает их, XOR переключает, а AND с инверсией маски очищает. В примере результаты выводятся в двоичном виде:
DIM value AS INTEGER = &B1010
DIM mask AS INTEGER = &B1100
PRINT $"{value AND mask:B4}"
PRINT $"{value OR mask:B4}"
PRINT $"{value XOR mask:B4}"
PRINT $"{value AND NOT mask:B4}"
Результат:
1000
1110
0110
0010
AND, OR, XOR, EQV и IMP вычисляют оба операнда слева направо и не пропускают правый по значению левого.
Ширина результата
Разрядность определяется типом, а не числом цифр в литерале: &HFFFFFFFF является положительным LONG 4294967295, поэтому его инверсия NOT &HFFFFFFFF обрабатывает 64 разряда и равна -4294967296. INTEGER со всеми единичными разрядами задаётся значением -1 или выражением NOT 0.
Нужный тип должен быть задан до операции: выражение CLNG(1) << 32 сдвигает LONG и даёт 4294967296, а объявление DIM wide AS LONG = 1 << 32 сначала сдвигает INTEGER и только затем расширяет результат 1 до LONG.
Сдвиг влево и вправо
<целое значение> << <целое количество разрядов><целое значение> >> <целое количество разрядов>
Левый операнд INTEGER или LONG задаёт значение и тип результата, правый операнд INTEGER или LONG — количество разрядов. Сдвиг влево отбрасывает вышедшие за ширину биты и добавляет нули справа; сдвиг вправо сохраняет знак, заполняя старшие позиции отрицательного числа единицами, а неотрицательного — нулями.
Количество после получения INTEGER должно находиться от 0 до 63, иначе выдаётся MB3005. Для INTEGER используются только пять младших битов допустимого количества: 32 действует как 0, 33 как 1, 63 как 31; для LONG каждое количество от 0 до 63 используется непосредственно.
Если количество имеет тип LONG, сначала сохраняются лишь его младшие 32 бита, затем проверяется полученный INTEGER: поэтому 4294967296 и -4294967296 превращаются в 0, а 4294967297 превращается в 1. Проверяется полученное количество, а не исходная 64-разрядная величина, поэтому количество задаётся непосредственно в пределах от 0 до 63.
| Выражение | Результат |
|---|---|
| 1 << 31 | -2147483648 |
| 1 << 32 | 1 |
| CLNG(1) << 32 | 4294967296 |
| CLNG(1) << 63 | -9223372036854775808 |
| -8 >> 2 | -2 |
| -1 >> 1 | -1 |
| 1 << 64 | MB3005 |
Сдвиг не проверяет арифметическое переполнение: сложение INTEGER 2147483647 + 1 вызывает MB3008, а 2147483647 << 1 возвращает -2 после отбрасывания вышедших разрядов.
Сочетание с другими операциями
Степень ^ имеет больший приоритет, чем NOT, а сам NOT выполняется раньше умножения, сложения, сдвигов и сравнений. Сдвиги связываются слабее сложения, но сильнее сравнений; затем следуют AND, XOR, OR, EQV и IMP.
Скобки явно отделяют маску от проверки: (value AND mask) <> 0. Полный порядок приведён в таблице приоритетов.
Диагностика
MB2003: неподходящий тип операнда, например смесь BOOLEAN с INTEGER или количество сдвига типа DOUBLE.MB3005: количество после преобразования в INTEGER находится вне диапазона от 0 до 63.MB2018: литерал не помещается в ожидаемый целочисленный тип.MB3008: переполнение соседней арифметической операции; отбрасывание разрядов при сдвиге этим кодом не диагностируется.
Запускаемые примеры
См. также
Для разрядов и сдвигов служат системы счисления и операции и приоритет. Логические значения описаны в статье BOOLEAN.