Позволяет пользователю дать новое имя некоторому типу

Лекция 2.

Замечание. typedef и #define

=typedef позволяет пользователю дать новое имя некоторому типу:

typedef тип новое_имя [размерность];

Введенное новое имя можно использовать также, как имена стандартных типов. typedef unsigned int Uint;

Uint k, I, j;

=Директива #defineявляется директивой препроцессора и определяет некоторую подстановку в тексте программы, выполняемую препроцессором.#defineиспользуется для определения:

· символических констант: #define имя текст_подстановки

Каждое вхождение имени заменяется на текст_подстановки.

#define VER 1 {в С++ лучше писпользовать const ….}

· символов, управляющих условной компиляцией: #define имя

#define HEADER_INCLUDED

Используется вместе с директивами #ifdef и #ifndef .

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

Пример. X=3.5 = 11.12 = (-1)0 *1.11*21 P’=P+1023 P’ = 1+1023 =1024 p’=1024 f=0.11 …

7 6 5 4 3 0

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

Число –3.5: С0 0С 00 00 00 00 00 00

Диапазон чисел, представимых в формате с плавающей точкой (тип double): |X|min<=|X|<=|X|max и X=0 1<=P’<=2046 (для чисел X ≠0)

|M|min*2pmin <=|X| <=|M|max*2pmax и X=0

1*2-1022<=|X| <=(2-2-52)*21023 и X=0

10k<=|X| <=10L lg2=0.30103

k= –1022*lg 2= –307.65266= -308+0.34734

L= 1024* lg 2=1024*0.30103=308.25472

2.2*10-308<=|X| <=1.7*10308 и X=0

__[\\\\\\\\] _____|_____[\\\\\\\\]______

Xmax -Xmin 0 Xmin Xmax

Точность чисел, представленных в формате с плавающей точкой: часто вместо числа Х в МС хранится его приближение Х*. Погрешность вносится из-за хранения приближенного значения мантиссы.

Абсолютная погрешность числа Х*:

|X – X*| = ∆( Х*) = |MX –MX*|*2px = 2-52*2px

т.е. абсолютная погрешность числа зависит от порядка числа. Обычно для формата с плавающей точкой определяют относительную погрешность Х* : d(Х*)

Для пользователя более важным является практический вопрос: сколько значащих цифр десятичного представления числа гарантированно сохраняются при таком формате хранения числа. Есть приближенное правило для определения этого количества К цифр при q=2:

m двоичных разрядов мантиссы соответствуют К десятичным цифрам:

К»[m / 3.32]; Для типа double К=[53/3.32]=15.96, т.е. для значения типа

doubleсохраняется в памяти 15-16 десятичных знаков.

Выполнение операций над числами, представленными с плавающей точкой (говорят- в плавающей арифметике).

Пусть X = Mx×2Px ,а Y = My×2Py

a)Сложение (вычитание) чисел:

Z = X ± Y = Mx×2Px ± My×2Py

={1шаг–выравнивание порядков к большему; пусть Px >Py}

= 2Px×(Mx ± My×2Py–Px)

={2 шаг–сдвиг мантиссы My на |Py–Px| разрядов}

= 2Px×mz

= {3шаг–сложение(вычитание) мантисс, получается мантисса mz}.

= Mz ×2Pz

Возможны случаи:

· 1 £ |mz| < 2 Þ операция закончена, Mz = mz ; Pz = Px ;

 

· 2 £ |mz|, но |mz| <4

Þ выполняется нормализация результата сдвигом мантиссы вправо на 1 разряд с коррекцией порядка (+1);

· |mz| <1 Þ выполняется нормализация результата сдвигом мантиссы влево на t разрядов с коррекцией порядка (–t).

з pх 1.ххххх

– з py 1.xxyyy пусть pх == py

з pх 0.00zzz При вычитании близких чисел

з pz 1.zz??? происходитпотеря точности

б)Умножение :

Z = X × Y = Mx×2Px × My×2Py = Mx×My×2Px+Py == Mz ×2Pz , где Mz = Mx×My ; Pz = Px+Py; т.е. при умножении чисел их мантиссы перемножаются, а порядки складываются. При умножении двух мантисс может получиться результат такой, что потребуется сдвиг мантиссы вправо, но не более, чем на один разряд, тогда нужна коррекция порядка (+1).

в)Деление :

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

Особые ситуации плавающей арифметики.

1. Переполнение порядка при выполнении операций плавающей арифметики;

 

2. некорректность деления в плавающей арифметике: деление на число с нулевой мантиссой;

 

3. потеря значимости: Pz ¹0, а Mz = 0;

 

4. исчезновение порядка: Pz < Pmin , а Mz ¹ 0.

 

Две последние ситуации не являются аварийными, они обычно приводят к тому, что результат Z заменяется нулём – это машинный нуль.

Достоинства формы представления чисел с плавающей точкой.

· Хранение только значащих цифр числа. Представление обеспечивает для числа максимальную точность при фиксированной разрядной сетке. Недостатки формы представления чисел с плавающей точкой: · более сложная конструкция схем, выполняющих операции над числами в процессоре: необходимы отдельные схемы для…

Объединения.

  Размер объединения равен максимальной из длин его полей. Описание объединения напоминает описание структуры:

Int i, j , m;

i=0; //индекс байта строки u.

q.a = n; //значение n помещается в объединение q

m=L<<3 –1; //индекс последнего бита строки s.

Do

{unsigned char x;

x=q.u[i++]; //запись в x очередного i-го байта из u

for( j=0; j<8; j++) // цикл формирует биты от 0 до7

{s[m--] = (x&1)+'0'; // i-го байта строки s.

x=x>>1;

}

}

while (i<L);

s[L<<3]=0; //нуль символ в конец строки s.

}

 

//Внутреннее 16-ричное представление целого.

void hexkod (tip n, char s[ ])

{int i,m;

unsigned char x;

char alf[ ]="0123456789ABCDEF";//алфавит 16-ой системы счисления

i=0; m=L<<1 –1;

do

{x=q.u[i++];

s[m--]=alf[x&15]; // младшая 16-ричная цифра i-го байта u

s[m--]=alf[(x>>4)]; // старшая 16-ричная цифра i-го байта u

}

while (i<L);

s[L<<1]=''; //нуль символ в конец строки s.

}

 

int main ()

{double x;

char t[L<<3+1];

cout<< "n double—x= ";

cin>> x;

 

binkod(x,t); cout<< x<< ": n"<<t<<endl;

hexkod (x,t); cout<< x<< ": "<<t<<endl;

 

return 0;

}

 

Выполнение этой программы

double—-x= 3.5

3.5:

3.5: 400C000000000000

 

Замечание 1. Этот способ доступа к значению типа tip (с union ) может быть применён для значения другого простого типа, например, int. Это более универсальный способ. Для использования этой программы для типа int достаточно изменить одно предложение:

typedef int tip;

В главе «Битовые операции» для получения внутреннего кода данных типа int был рассмотрен другой способ с использованием битовых операций и операций сдвига. Тот способ не приемлем для вещественных данных, так как для них не определены битовые операции и операции сдвига.

 

Замечание 2. Шаблон функции.

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

 

Простейшая функция-шаблон с параметром tip имеет вид:

template <class tip> заголовок_функции

{/*тело функции*/}

Пример шаблона функции с параметром tip для получения строки внутреннего двоичного кода данного этого типа.

 

#include <iostream>

Using namespace std;

template <class tip>

void binkod (tip n,char s[])

{//функция формирует строку s с двоичным представлением //вещественного данного n.

const int L=sizeof(tip); // размер типа в байтах

union {

Tip a;

unsigned char u[L];

}q;

Int i,j,m;

i=0; //индекс байта строки u.

q.a=n; //значение n помещается в объединение q

m=L<<3–1; //индекс бита строки s.

Do

{unsigned char x;

x=q.u[i++]; //запись в x очередного байта из u

for( j=0;j<8;j++) // цикл формирует биты от 0 до7

{s[m--]=(x&1)+'0'; // m-го бита строки s.

x=x>>1;

}

}

while (i<L);

s[L<<3]=0; //нуль символ в конец строки s.

}

template <class tip>

void hexkod(tip n,char s[])

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

const int L=sizeof(tip);

union {

Tip a;

unsigned char u[L];

}q;

Int i,m;

Unsigned char x;

char alf[]="0123456789ABCDEF";

q.a=n;

i=0; m=L<<1–1;

Do

{x=q.u[i++];

s[m--]=alf[x&15];

s[m--]=alf[(x>>4)];

}

while (i<L);

s[L<<1]='';

}

Int main ()

{const int Q=sizeof(long double);

Int n;

Double u;

Float v;

Bool p;

cout<< "n int--n "; cin>> n; cout<< "n double--u ";

1023: 00000000000000000000001111111111

FF

-3.5: 1100000000001100000000000000000000000000000000000000000000000000

-3.5: C00C000000000000

2.5: 01000000001000000000000000000000

2.5: 40200000

1: 00000001

1: 01

 

 

//вывод 16-го кода с использованием манипуляторов

Cout.precision(8);

cout.fill('0'); for(int i=7;i>=0;i--) cout<<hex<<setw(2)<<int(q.u[i])<<' ';