Что такое операторы побитового сдвига (bit-shift) и как они работают?

вы можете использовать BCP для небольших таблиц.

Команда BCP OUT: -

BCP "SELECT *  FROM [Dinesh].[dbo].[Invoices]" QUERYOUT C:\av\Invoices1.txt -S MC0XENTC -T -c -r c:\error.csv

Команда BCP IN: - Создать структуру таблицы для Invoicescopy1.

BCP [Dinesh].[dbo].[Invoicescopy1] IN C:\av\Invoices.txt -S MC0XENTC -T -c
1305
задан msanford 11 December 2018 в 19:27
поделиться

4 ответа

Операторы смещения бита делают точно, что подразумевает их имя. Они смещают биты. Вот является резюме (или not-so-brief) введением в различные операторы сдвига.

Операторы

  • >> арифметика (или подписанный) оператор сдвига вправо.
  • >>> логическое (или неподписанный) оператор сдвига вправо.
  • << левый оператор смещения и удовлетворяет потребности и логических и арифметических сдвигов.

Все эти операторы могут быть применены к целочисленным значениям (int, long, возможно short и byte или char). На некоторых языках, применяя операторы сдвига к любому типу данных, меньшему, чем [1 119] автоматически, изменяет размеры операнда, чтобы быть int.

Примечание, которое <<< не является оператором, потому что это было бы избыточно.

Также примечание, что C и C++ не различают операторы сдвига вправо . Они обеспечивают только >> оператор, и смещающее право поведение является реализацией, определенной для типов со знаком. Остальная часть ответа использует операторы C# / Java.

(Во всей господствующей тенденции C и реализациях C++ включая gcc и clang/LLVM, >> на типах со знаком арифметика. Некоторый код принимает это, но это не что-то стандартные гарантии. Это не не определено , хотя; стандарт требует, чтобы реализации определили его так или иначе. Однако сдвиги влево отрицательных чисел со знаком неопределенное поведение (переполнение целого числа со знаком). Таким образом, если Вам не нужен арифметический сдвиг вправо, это обычно - хорошая идея сделать Ваше смещение бита с неподписанными типами.)

<час>

Сдвиг влево (< <)

Целые числа хранятся, в памяти, как ряд битов. Например, номер 6, сохраненный как 32-разрядное int, был бы:

00000000 00000000 00000000 00000110

Смещение этой комбинации двоичных разрядов налево одно положение (6 << 1) привело бы к номеру 12:

00000000 00000000 00000000 00001100

, Как Вы видите, цифры сместились налево одним положением, и последняя цифра справа заполнена нулем. Вы могли бы также отметить, что оставленное смещение эквивалентно умножению полномочиями 2. Так 6 << 1 эквивалентно [1 127], и 6 << 3 эквивалентно [1 129]. Хороший оптимизирующий компилятор заменит умножение сдвигами, если это возможно.

Непроспект, смещающийся

Обратите внимание на то, что, это не циклические сдвиги. Смещение этого значения налево одним положением (3,758,096,384 << 1):

11100000 00000000 00000000 00000000

результаты в 3,221,225,472:

11000000 00000000 00000000 00000000

цифра, которая смещается "от конца", потеряна. Это не повторяется.

<час>

Логический сдвиг вправо (>>>)

А логический сдвиг вправо является обратным к сдвигу влево. Вместо движущихся битов налево, они просто перемещаются направо. Например, смещение номера 12:

00000000 00000000 00000000 00001100

направо одним положением (12 >>> 1) возвратит наш оригинал 6:

00000000 00000000 00000000 00000110

, Таким образом, мы видим, что смещение направо эквивалентно подразделению полномочиями 2.

Потерянные биты уводят

Однако, сдвиг не может исправить "потерянные" биты. Например, если мы смещаем этот шаблон:

00111000 00000000 00000000 00000110

левым 4 положения (939,524,102 << 4), мы добираемся 2,147,483,744:

10000000 00000000 00000000 01100000

и затем смещающийся назад ((939,524,102 << 4) >>> 4) мы добираемся 134,217,734:

00001000 00000000 00000000 00000110

Мы не можем возвратить наше исходное значение, как только мы потеряли биты.

<час>

Арифметический сдвиг вправо (>>)

арифметический сдвиг вправо точно похож на логический сдвиг вправо, кроме вместо того, чтобы дополнить нулем, это дополняет старшим значащим битом. Это вызвано тем, что старший значащий бит , знак укусил, или бит, который отличает положительные и отрицательные числа. Путем дополнения старшим значащим битом арифметический сдвиг вправо является сохранением знака.

, Например, если мы интерпретируем эту комбинацию двоичных разрядов как отрицательное число:

10000000 00000000 00000000 01100000

у нас есть номер-2 147 483 552. Смещение этого к правильным 4 позициям с арифметическим сдвигом (-2 147 483 552>> 4) дал бы нам:

11111000 00000000 00000000 00000110

или номер-134 217 722.

, Таким образом, мы видим, что сохранили знак наших отрицательных чисел при помощи арифметического сдвига вправо, а не логического сдвига вправо. И еще раз, мы видим, что выполняем подразделение полномочиями 2.

1632
ответ дан Peter Cordes 11 December 2018 в 19:27
поделиться

Один глюк - то, что следующее является зависящим от реализации (согласно стандарту ANSI):

char x = -1;
x >> 1;

x может теперь быть 127 (01111111) или все еще-1 (11111111).

На практике, это обычно - последний.

27
ответ дан AShelly 11 December 2018 в 19:27
поделиться

Битовые операции, включая сдвиг разряда, фундаментальны для аппаратных средств низкого уровня или встроенного программирования. При чтении спецификации для устройства или даже некоторых форматов двоичного файла Вы будете видеть байты, слова, и dwords, разбитый в небайт, выровнял битовые поля, которые содержат различные значения интереса. Доступ к этим битовым полям для чтения/записи является наиболее распространенным использованием.

А простой реальный пример в графическом программировании - то, что 16-разрядный пиксель представлен следующим образом:

  bit | 15| 14| 13| 12| 11| 10| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1  | 0 |
      |       Blue        |         Green         |       Red          |

Для достигания зеленого значения Вы сделали бы это:

 #define GREEN_MASK  0x7E0
 #define GREEN_OFFSET  5

 // Read green
 uint16_t green = (pixel & GREEN_MASK) >> GREEN_OFFSET;

Объяснение

для получения значения зеленого цвета ТОЛЬКО, который запускается при смещении 5 и заканчивается в 10 (т.е. 6 битов длиной), необходимо использовать (разрядную) маску, которая, когда применено против всего 16-разрядного пикселя, приведет только к битам, которыми мы интересуемся.

#define GREEN_MASK  0x7E0

соответствующая маска является 0x7E0, который в двоичном файле равняется 0000011111100000 (который является 2016 в десятичном числе).

uint16_t green = (pixel & GREEN_MASK) ...;

Для применения маски Вы используете операцию И (&).

uint16_t green = (pixel & GREEN_MASK) >> GREEN_OFFSET;

После применения маски, Вы закончите с 16-разрядным числом, которое является действительно просто 11-разрядным числом, так как его MSB находится в 11-м бите. Зеленый на самом деле только 6 битов длиной, таким образом, мы должны масштабировать его вниз использование сдвига вправо (11 - 6 = 5), следовательно использование 5, как смещено (#define GREEN_OFFSET 5).

Также распространенный использует сдвиги разряда для быстрого умножения и разделения полномочиями 2:

 i <<= x;  // i *= 2^x;
 i >>= y;  // i /= 2^y;
96
ответ дан Peter Mortensen 11 December 2018 в 19:27
поделиться

Скажем, у нас есть единственный байт:

0110110

Применение единственного левого сдвига разряда получает нас:

1101100

крайний левый нуль был смещен из байта, и новый нуль был добавлен к правильному концу байта.

биты не делают трансформации; они отбрасываются. Это означает, не вернете ли Вы сдвиг влево 1101100 и затем сдвиг вправо это, Вы тот же результат.

Смещение, оставленное N, эквивалентно умножению на 2 <глоток> N .

Смещающееся право N (если Вы используете дополнение ), эквивалент деления на 2 <глоток> N и округление для обнуления.

Bitshifting может использоваться для безумно быстрого умножения и разделения, если Вы работаете с питанием 2. Почти все стандартные программы низкокачественной графики используют bitshifting.

, Например, путь назад в былые дни, мы использовали 13-й режим (320x200 256 цветов) для игр. В 13-м Режиме видеопамять была размечена последовательно на пиксель. Это означало вычислять местоположение для пикселя, Вы будете использовать следующую математику:

memoryOffset = (row * 320) + column

Теперь, назад в тот день и возраст, скорость была очень важна, таким образом, мы будем использовать сдвиги разряда, чтобы сделать эту операцию.

Однако 320 не питание два, так для обхождения этого мы должны узнать то, что является питанием два, который добавил, вместе делает 320:

(row * 320) = (row * 256) + (row * 64)

Теперь мы можем преобразовать это в сдвиги влево:

(row * 320) = (row << 8) + (row << 6)

Для конечного результата:

memoryOffset = ((row << 8) + (row << 6)) + column

Теперь мы получаем то же смещение как прежде, кроме вместо дорогой операции умножения, мы используем эти два сдвига разряда... в x86, это было бы что-то вроде этого (примечание, это было навсегда, так как я сделал блок (примечание редактора: исправленная пара ошибок и добавила 32-разрядный пример)):

mov ax, 320; 2 cycles
mul word [row]; 22 CPU Cycles
mov di,ax; 2 cycles
add di, [column]; 2 cycles
; di = [row]*320 + [column]

; 16-bit addressing mode limitations:
; [di] is a valid addressing mode, but [ax] isn't, otherwise we could skip the last mov

Общее количество: 28 циклов на любом древнем ЦП имели эти синхронизации.

циклы Vrs

mov ax, [row]; 2 cycles
mov di, ax; 2
shl ax, 6;  2
shl di, 8;  2
add di, ax; 2    (320 = 256+64)
add di, [column]; 2
; di = [row]*(256+64) + [column]

12 на том же древнем ЦП.

Да, мы работали бы это трудно для бритья 16 циклов ЦП.

В 32 или 64-разрядный режим, обе версии становятся намного короче и быстрее. Современные центральные процессоры выполнения с изменением последовательности как Intel Skylake (см. http://agner.org/optimize/ ) имеют очень быстрые аппаратные средства, умножаются (низкая задержка и высокая пропускная способность), таким образом, усиление намного меньше. Семья бульдозера AMD немного медленнее, специально для 64-разрядного умножаются. На Intel CPUs и AMD Ryzen, два сдвига являются немного более низкой задержкой, но больше инструкций, чем умножение (который может вести для понижения пропускной способности):

imul edi, [row], 320    ; 3 cycle latency from [row] being ready
add  edi, [column]      ; 1 cycle latency (from [column] and edi being ready).
; edi = [row]*(256+64) + [column],  in 4 cycles from [row] being ready.

по сравнению с [1 137]

mov edi, [row]
shl edi, 6               ; row*64.   1 cycle latency
lea edi, [edi + edi*4]   ; row*(64 + 64*4).  1 cycle latency
add edi, [column]        ; 1 cycle latency from edi and [column] both being ready
; edi = [row]*(256+64) + [column],  in 3 cycles from [row] being ready.

Компиляторы сделают это для Вас: Посмотрите как gcc, лязг и MSVC все использование shift+lea при оптимизации return 320*row + col; .

самая интересная вещь отметить вот состоит в том, что x86 имеет инструкцию shift-and-add (LEA) , который может сделать маленькие сдвиги влево и добавить одновременно с представлением в качестве и add инструкция. ARM еще более мощен: один операнд любой инструкции может быть лев или прав смещенный бесплатно. Так масштабируясь постоянным во времени компиляцией это, как известно, является power-2, может быть еще более эффективным, чем умножение.

<час>

хорошо, назад в современные дни... что-то более полезное теперь должно было бы использовать bitshifting для хранения двух 8-разрядных значений в 16-разрядном целом числе. Например, в C#:

// Byte1: 11110000
// Byte2: 00001111

Int16 value = ((byte)(Byte1 >> 8) | Byte2));

// value = 000011111110000;

В C++, компиляторы должны сделать это для Вас, если бы Вы использовали struct с двумя 8-разрядными участниками, но на практике сделайте не всегда.

195
ответ дан Peter Cordes 11 December 2018 в 19:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: