Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2013 / БитовыеОперацииЛекция 1.docx
Скачиваний:
30
Добавлен:
23.02.2015
Размер:
73.31 Кб
Скачать

Лекция 1. Битовые операции.

Битовые (поразрядные) операцииприменяются к целым операндам и выполняются над их двоичными представлениями. Стандартные типы , кроме вещественных, в С++ являются целыми, т.е. к данным этих типов (int, short, char, bool) могут быть применены поразрядные операции. Выполняется битовая операция над соответствующими битами операндов и результат операции записывается в соответствующий бит результата.

Рисунок 1. Схема битовой операции

Операции:

&- поразрядная конъюнкция (И) двоичных представленийоперандов целого типа. Бит результата равен 1 тогда и только тогда, если соответствующие биты операндов равны 1.

7 & 5= 5 7: 00000111

5: 00000101

Результат 5:00000101

Операция используется для выделения из битового представления части разрядов, такие разряды умножаются на 1 (по операции &).

| – поразрядная дизъюнкция (ИЛИ) двоичных представлений операндов целого типа. Бит результата равен 1, если хотя бы один из соответствующих битов операндов равен 1.

7 | 5= 7 7: 00000111

5: 00000101

Результат 7: 00000111

Операция используется для приформирования 1 некоторым разрядам битового представления, к этим разрядам добавляются 1 (по операции |).

^ - поразрядное исключающее ИЛИ двоичных представлений операндов целого типа. Бит результата равен 1, если соответствующие биты операндов различны.

7 ^ 5= 2 7: 00000111

5: 00000101

Результат 2: 00000010

Операция используется для инвертирования некоторых разрядов битового представления, такие разряды в операции исключающего ИЛИ обрабатываются 1.

~ - поразрядное отрицание (одноместная операция) инвертирует каждый разряд двоичного представления операнда.

~ 5 =-6 5: 00000101

Результат - 6 : 11111010 –дополнительный код числа -6

Операции сдвига

Применяются к целочисленным операндам:

ОП1 << ОП2 (сдвиг влево) или ОП1 >> ОП2(сдвиг вправо)

Двоичное представление ОП1 сдвигается влево (<<)) или вправо (>>) на количество двоичных разрядов, равное второму операнду ОП2.

При сдвиге вправоосвободившиеся разряды заполняются знаковым разрядом (при сдвиге знаковых операндов) и нулём для беззнаковых. Вышедшие за разрядную сетку биты теряются.

5>>2 = 1 00000101 =>00000001 - результат = 1

(-5)>>2 = -2 11111011 =>11111110 - дополнительный код -2.

При сдвиге влево освободившиеся разряды заполняются нулями.

5<<2 = 00000101 =>00010100 - результат = 20

(-5)<<2 = 11111011 =>11101100 - дополнительный код -20

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

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

cout<<(2147483647<<1)<<" "<<(-2147483647<<1)<<'\n'; // результат -2 2

Не следует путать битовые операции с логическими:

!0 – имеет значение истины, а ~0 представляет конечную последовательность единиц(т.е. -1).

cout<<(!0)<<" "<<(~0)<<'\n'; результат : 1 -1

Примеры использования битовых операций:

unsigned int n,m; (или int n,m; но n,m –не отрицательные)

  1. If (n&1){ . . .} – проверяет, является ли n нечётным.

  1. If (n&m){ . . .} - проверяет, есть ли у n и m совпадающие соответствующие биты.

  1. If (n^m){ . . .} - проверяет, есть ли у n и m несовпадающие соответствующие биты.

  1. If (n&(n-1)){ . . .}– проверяет, что n не является степенью 2. Если n есть степень 2, то n представляется в двоичном виде:

n: 00…01000…000,

а n-1: 00…00111…111,

их произведение n&(n-1)=0.

Битовые операции и операции сдвига используются часто

  • в выражениях, где они предпочтительнее других операций, т.к. выполняются быстрее;

  • для реализации множеств небольшого размера. Множество представляется в виде конечной последовательности битов (вектора битов), бит есть элемент множества, каждому биту соответствует элемент некоторого, например, массива чисел, размер множества чисел ограничен количеством битов в векторе.

A[n-1]A[n-2] . . . . . . . . . . . . . . . . . . . . A[2] A[1] A[0]

1

0

0

0

1

1

0

Оператор typedef служит для того, чтобы задать новое имя некоторому типу: typedef тип новое_имя [размерность];

Например:

typedef int tip; // стандартный тип int назван новым именем tip. Алгоритм описывается для этого нового имени tip. В дальнейшем, если необходимо алгоритм использовать для нового имени, например short, надо будет изменить только оператор typedef: typedef short tip;

Размерность необязательна. Новое имя может использоваться также, как стандартные имена:

typedef char tipmas [50]; // tipmas это char[50]

tipmas M[20]; M – это массив из 20 строк по 50

символов. (char MM [20][50])

const int L=sizeof(tip);

//Внутреннее представление целых данных

typedef int tip;

Void inint (tip n, char s[])

{ // Двоичное представление целого данного n в строке s

int i, l;

l=sizeof(tip)<<3; // размер битовой строки для типа tip

i=l-1; // l-ый разряд- конец строки

do

{ s[i--]=(char)((n&1)+'0'); //формирование i-го символа

n=n>>1; //сдвиг числа n вправо

}

while (i>=0); //цикл пока все символы не сформируются

s[l]=0; //символ 0 в конце сформированной строки

}

Void inint16(tip n, char s[])

{// 16-теричное представление целого n в строке s.

int i,l;

char alf[]="0123456789ABCDEF"; // 16-ричный алфавит

l=sizeof(tip)<<1; //размер 16-теричной строки для tip

i=l-1;

do

{s[i--]=alf[n&15]; //формирование i-го символа

n=n>>4; //сдвиг числа n вправо

}

while (i>=0); //цикл пока все символы не сформируются

s[l]='\0'; //символ 0 в конце сформированной строки

}

Пример. Внутреннее представление целого числа -123 в слове: - 123=-7В16= -111 10112

-123: 11111111 11111111 11111111 10000101

-123 в16-ом коде: FF FF FF 85

Алгоритм “Быстрая степень”

Рассмотрим возведение an, где n- целое, положительное число. Алгоритм основан на двоичном представлении показателя n.

n= nk*2k + nk-1*2k-1 +. . . . ni*2i+. . . .n1*21+ n0+20

Вычисление этого произведения представляется тремя параллельными процессами:

  • вычисление ni – цифр двоичного представления показателя n, они получаются как остатки от деления на 2 сначала n, а потом частных от делений, пока не получится частное, равное нулю, остатки в порядке получения n0 n1 n2 . . .nk-1 nk (см. перевод целого в двоичную систему).

  • параллельно с этим процессом строятся степени: a=a*a

.

  • Кроме того, происходит накопление произведения в некоторой переменной p (в начале p=1;): если остаток ni равен нулю, соответствуюшая степень не учитываертся в произведении, если ni ≠0, то p=p*,т.е. в этом алгоритме (быстром!) не все степени вычисляются, а из вычисленных не все учитываются.

p=1 а n |_2

a1 n0 | n|_2

a2 n1| n

. . . . . . .

p=p*,если ni ≠0

. . . . . .

, nk-1 |n |_2

a2k nk | 0

Пример. Найтиa18.

18=100102, т.е.в произведении строятся степениa1,a2,a4,a8,a16,

а учитываются две, p=1*a2*a16

#include <iostream>

using namespace std;

// функция возведения a в степень n

double power (double a, int n)

{double p=1;

while (n!=1)

{if (n&1) p*=a; // анализ очередной двоичной цифры

a*=a; //в переменной a получается очередная степень

n>>=1; // сдвиг n

}

return p*a;

}

// Рекурсивный вариант функции возведения a в степень n

double powerR (double a, int n)

{if (n==0) return 1;

if (n==1) return a;

if (n&1) return powerR (a*a, n>>1)*a;

else return powerR (a*a, n>>1);

}

int main (){

cout<<power(2.0,18)<<endl;

cout<<powerR(2.0,18)<<endl;

return 0;

}

Результаты:

262144

262144

Представление вещественных чисел в памяти ПК

В С++есть два вещественных типа: float и double. Операции

вещественной арифметики выполняет сопроцессор (FPU).

Значение, полученное в сопроцессоре, преобразуется в тип float или double и хранится в этом типе.

Вещественное число представляется в виде (в IBM PC):

x=(-1)S * 1.f * 2p

Здесь S – знак(‘+’ - 0/’-’ - 1), 1.f – мантисса числа, p – порядок.

В памяти хранится: машинный порядок P=P+д; д зависит от формата и кол-ва разрядов под порядок, 1 в целой части мантиссы в форматах float и double не хранится, но подразумевается, в сопроцессоре 1 в целой части мантиссы хранится.

S

---

P’->

<-

----

--f--

------

------

------

---

K

Под машинный порядок p’ отводится K разрядов: в эти к разрядов входит максимальное число max, д=max/2 , половина значений p’ соответствуют положительному истинному порядку p, половина отрицательному.P’ всегда положительный.

K=8 (float); K=11 (double); K=15(long double);

max=255 max=2047 max=32767

соответственно

д=127(float); д=1023(double); д=16383(long double);

p’= p+127 p’= p+1023 p’= p+16383

Для чисел X: 0< p’ <max;

Eсли P’=0 и f=0, то X=0.

Eсли P’=max, то данноеXне является числом, а представляет некоторое специальное значение:

+бесконечность : 0 11 . . .11 00. . . . 00

- бесконечность : 1 11 . . .11 00. . . . 00

P’ f

Не числа: (NaN, Not a Number):P’=11. . . 11, а f≠0

Неопределённость: P’=11. . . 11, а f=1000 . . . 00

Денормализованные числа: 0/1 P’=000. . . . 00, а f≠0

Положение точки не фиксируется, оно определяется порядком Pчисла, т.е. точка как бы плавает по разрядной сетке: для разных чисел её место может быть разным. Такая форма представления получила названиепредставления с плавающей точкой. Код числа в этом случае называют машинным кодом представления с плавающей точкой. Характеристики машинного слова: МС {n,k,m,q,E=2}, Е не хранится, оно выбирается при конструировании процессора, также как и способ представления порядкаP’.

В дальнейшем будем рассматривать тип double

Пример. X=3.5 = 11.12= (-1)0*1.11*21

P’ = 1+1023 =1024

p’=1024f=0.11

01000000

0000

1100

00000000

00000000

00000000

00000000

7 6 5 4 3 0

16-ый код: 40 0С 00 00 00 00 00 00