- •Київський національний університет імені Тараса Шевченка
- •Гриф надано Міністерством освіти і науки України (лист № 1.4/18-г-1523 від 20.09.07)
- •Основи алгоритмізації
- •1.1. Поняття алгоритму
- •1.2. Класифікація внутрішніх структур алгоритмів
- •1. Ввести а,b.
- •3. Вивести значення s.
- •4. Кінець.
- •6. Закінчити роботу.
- •1. Ввести число n.
- •6. Перехід до п.3.
- •7. Друк к.
- •8. Кінець.
- •1.3. Складність алгоритмів
- •1.4. Складність задач
- •Завдання для самостійної роботи
- •Форма Бекуса – Наура
- •Завдання для самостійної роботи
- •Void main(){
- •3.2. Структура с-програми
- •3.3. Описувачі
- •3.4. Основні операції мови с
- •If(!inword)
- •Void main()
- •Int rozmir;
- •3.5. Оператори мови с
- •3.5.1. Прості оператори
- •3.5.2. Умовний оператор
- •3.5.3. Оператор циклу for
- •3.5.4. Оператори do-while, while
- •3.5.5. Оператор continue
- •3.5.6. Оператор-перемикач switch
- •3.5.7. Оператор break
- •3.5.8. Оператор goto
- •If(error(I,j,k)) goto exit;
- •3.5.9. Оператор return
- •3.6. Директиви препроцесору та вказівки компілятору
- •3.6.1. Директива препроцесору #define
- •1. Макровизначення:
- •3.6.3. Директива #include
- •3.6.4. Директиви умовної компіляції #if, #elif, #else, #endif
- •3.6.5. Директива #line
- •If(!cond)
- •3.7. Описувачі з модифікаторами
- •3.7.1. Моделі пам'яті
- •3.7.2. Модифікатори типу доступу в пам'яті
- •Int huge*near X;
- •3.7.3. Модифікатори const, volatile, cdecl, pascal, interrupt
- •Volatile int t;
- •Void interrupt timer()
- •Void wait(int interval)
- •Завдання для самостійної роботи
- •Принципи типізації даних
- •4.1. Прості типи даних
- •4.2. Похідні типи
- •4.3. Еквівалентність типів
- •4.4. Успадкування атрибутів
- •4.5. Перераховні типи
- •4.6. Логічні типи
- •4.7. Символьні типи
- •4.8. Числові типи
- •4.9. Структурні типи даних
- •4.9.1. Масиви
- •4.9.2. Структури
- •Int year;
- •4.10. Деякі особливості типів даних c
- •4.10.1. Базові типи даних
- •4.10.2. Перетворення типів
- •Int atoi(char s[]) /*char* s*/
- •4.10.3. Засіб typedef
- •Int curs;
- •4.10.4. Покажчики та масиви
- •Void * p;
- •Int array[12];
- •Void f(int a[])
- •Int f(char * s)
- •Наведемо деякі приклади розв'язання задач.
- •Int shift; /*відступ*/
- •Int count[n]; /*кількість монет даного типу (коефіцієнти ai)*/
- •Int coin;
- •Int sum; /*монета, яку міняємо*/
- •Int maxcoin; /*індекс по масиву cost[] монети максимальної вартості, допустимої при даному розміні.*/
- •If(count[I])
- •If(maxcoin)
- •Int* ctranspon (int *a,int n,int m)
- •Void dobutok(int* a, int* b, int** c, int n, int m)
- •Int n,m,I,size;
- •Int main()
- •4.10.5. Структури та об'єднання
- •Розглянемо деякі приклади розв'язання задач.
- •Int hashfunc(key); int eqkey(key, key);
- •Void freeval(val); void setval(val, val);
- •Void freekey(key); void setkey(key, key);
- •Int hashfunc(key key){
- •Val val; /*значення*/
- •Void set(key key, val val){
- •Void printcell(struct cell *ptr){
- •Void main(void)
- •Завдання для самостійної роботи
- •Зображення чисел у комп'ютері
- •Int main(void)
- •5.1. Системи числення
- •5.2. Правила переведення чисел з однієї системи числення в іншу
- •5.3. Правило визначення точності зображення
- •5.4. Двійкова арифметика
- •5.4.1. Додавання двійкових чисел
- •5.4.2. Зображення від'ємних чисел
- •XXXXXXXX 00000001 00000000.
- •5.4.3. Віднімання двійкових чисел
- •5.4.4. Множення двійкових чисел
- •5.4.5. Ділення двійкових чисел
- •5.5. Ознака переповнення розрядної сітки при арифметичних операціях
- •5.6. Зображення цілих чисел
- •5.7. Зображення дійсних чисел
- •5.8. Керування машинним зображенням чисел та особливості виконання арифметичних операцій
- •Завдання для самостійної роботи
- •Реалізація концепції структурного програмування
- •6.1. Оголошення та визначення функцій
- •Int d;} people;
- •6.2. Формальні та фактичні параметри
- •Void swap(int a,int b)
- •Void swap(int a,int*b)
- •6.3. Функції зі змінною кількістю параметрів
- •Void sum(char *msg,...)
- •6.5. Параметри функції main
- •6.6. Лiтернi покажчики та функцiї
- •Void strcpy(char*s,char*t)
- •Void f(void)
- •6.8. Класи пам'яті
- •Розглянемо деякі приклади розв'язання задач.
- •I, power(2,I),power(-3,I));
- •Void main() { choturukyt b; tochka *a; float s; long n,in; srand(time(null));
- •6.9. Введення–виведення с. Файли та потоки
- •6.9.1. Функції введення–виведення верхнього рівня
- •6.9.2. Функції введення–виведення консольного термінала та порту
- •Int main(void)
- •6.9.3. Функції введення–виведення нижнього рівня
- •Int main(void)
- •Int handle;
- •Розглянемо приклади розв'язання задач.
- •Void main(void)
- •Void main(argc,argv)
- •If(c& masks[I])
- •If (цей рядок довший за найдовший з попередніх)
- •Int max; /*максимальна довжина*/
- •Int len; /*довжиною цього рядка*/
- •Int nwords; /*кількість слів у рядку*/
- •If(!*s) /*рядок закінчився*/
- •Int ctr; /*кількість входжень слова*/
- •If(!strcmp(word,w[I].Wrd)){
- •If(alert){
- •Void main() { float X,y,z,t,s; int I,j,flag,n,k; m1: clrscr();
- •InitBase (void){
- •Int key, /*новий ключ*/
- •InitBase();
- •Завдання для самостійної роботи
- •7.1. Елементи концепції обєктно-орієнтованого програмування
- •Int year;
- •Int year;
- •7.3. Опис протоколу класу
- •7.4.1. Коментарі
- •7.4.2. Прототипи функцій
- •Void f();
- •7.4.5. Перевантаження функцій
- •Int Name (int first)
- •Int Name (unsigned first)
- •Int Name (int first,char*second)
- •7.4.6. Значення формальних параметрів за умовчанням
- •7.4.7. Посилання й покажчики
- •Void increment(int& X)
- •Int anotherint;
- •7.4.10. Покажчик на void
- •Void*void_ptr;
- •Void swap(void*&item1,void*&item2)
- •7.4.11. Зв'язування зі збереженням типів
- •7.4.12. Про структури та об'єднання
- •7.5. Функції-члени класу
- •X *this;
- •Int year;
- •7.6. Конструктори та деструктори
- •7.6.1. Поняття про конструктори
- •Int*data;
- •Int size;
- •7.6.3. Конструктор копіювання
- •Int data[large];
- •Inline Large1 Large1::fast(const Large1 & b)
- •7.7. Глобальні та локальні об'єкти
- •Void main(void)
- •7.8. Статична пам'ять і класи
- •Int statpol::I;
- •Vоid draw()
- •Int large;
- •Int bigwant;
- •Void f() {
- •Void g(int a)
- •7.9. Успадкування
- •7.9.1. Синтаксична реалізація успадкування
- •7.9.2. Правила доступу до полів даних
- •Void f(void)
- •Void g(void){}//...}
- •7.9.3. Конструктори та деструктори в похідних класах
- •7.9.4. Використання заміщуючих функцій-членів
- •Void Display (void); //замiщувальна функцiя
- •Void Region::Display(void)
- •Void Display(void);};
- •Void Population::Display(void)
- •7.9.5. Похідні класи й покажчики
- •7.9.6. Ієрархія типів
- •XyValue(int_x,int_y):X(_x),y(_y)
- •XyData(int_x,int_y)
- •7.9.7. Множинне успадкування
- •Void SetLoc(int_x,int_y);};
- •Int data;
- •7.10. Віртуальні функції та класи
- •7.10.1. Віртуальні функції
- •Int value;
- •Virtual int GetValue();
- •Int Value::GetValue(){return value;}
- •7.10.2. Чисті віртуальні функції. Абстрактні класи
- •Virtual void f1(void);
- •Virtual void f2(void);//...}
- •Int index;
- •7.10.3. Віртуальні деструктори
- •7.10.4. Посилання як засіб для реалізації поліморфізму
- •7.10.5. Дещо про механізм віртуальних функцій
- •Virtual int method1(float r);
- •Int data;
- •Void func(void){//тіло}};
- •Virtual public CocaCola {
- •Int size;
- •Void ShowValue(void)
- •Void ShowValues(void);};
- •Void Two::ShowValues(void)
- •7.11.2. Дружні функції
- •Void Show(One &c1,Two &c2)
- •Void Show(One &c1);
- •Void Two::Show(One &c1)
- •7.12. Перевантаження операцій
- •7.12.1. Загальний підхід
- •Void main()
- •7.12.2. Перетворення типів
- •X::operator т();
- •7.12.3. Перевантаження операції індексування масиву
- •Int znach;
- •7.12.4. Перевантаження операції виклику функції
- •Int operator()(void);
- •Int FuncClass::operator()(void)
- •Vidnosh*vec;
- •7.12.5. Перевантаження операції доступу до члена класу
- •7.12.6. Перевантаження операцій інкремента й декремента
- •Int index;
- •Void*operator new(size_t)
- •Void*operator new(size_t);
- •8.1. Функціональні шаблони
- •8.1.1. Визначення й використання шаблонів функцій
- •Void func(t t)
- •Int main(void)
- •8.1.2. Перевантаження шаблонів функції
- •Int main(void)
- •8.1.3. Cпецiалiзованi функцiї шаблона
- •Int main(void){
- •8.2. Шаблони класів
- •8.2.1. Визначення шаблонів класу
- •Void push(t t);
- •Int numitems;
- •8.2.2. Константи й типи як параметри шаблона
- •8.2.3. Використання шаблонних класів
- •Int main(void)
- •8.2.4. Спеціалізація шаблонів класу
- •Void add(t item);
- •Int main(void)
- •IArray.Add(i1);
- •Int main(void)
- •IList.Add(i1);
- •Завдання для самостійної роботи
- •Автоматна технологія програмування
- •If(!stop)printf("не входити");
- •Завдання для самостійної роботи
- •Список літератури
- •Передмова 3
5.8. Керування машинним зображенням чисел та особливості виконання арифметичних операцій
Співпроцесор майже завжди виконує всі операції у форматі long double. Він має спеціальний керуючий двобайтовий регістр. Установлення окремих бітів регістра диктує співпроцесору ту чи іншу поведінку. Його біти відповідають за те, як будуть округлятися числа, як співпроцесор розуміє нескінченність. Розглянемо тільки два біти регістра – восьмий і дев'ятий. Саме вони визначають, як оброблятимуться числа всередині співпроцесора.
Якщо восьмий біт містить одиницю (так установлено за умовчанням), то 10 байтів внутрішніх регістрів співпроцесора використовуватимуться повністю, і ми одержимо повноцінний long double. Якщо ж цей біт дорівнює нулю, то все визначається значенням дев'ятого біта. Якщо він дорівнює одиниці, то використовуються 53 розряди мантиси (інші завжди дорівнюють нулю). Якщо ж цей біт дорівнює нулю – то лише 24 розряди мантиси. Це збільшує швидкість обчислень, але зменшує точність. Іншими словами, точність роботи співпроцесора може бути знижена до типу double чи навіть float. Однак це стосується тільки мантиси, порядок завжди буде містити 15 бітів, так що діапазон типу long double зберігається в будь-якому випадку.
Сучасні співпроцесори обробляють числа з такою швидкістю, що навряд чи в кого-небудь може виникнути необхідність у прискоренні за рахунок точності. Проте це треба знати.
Ми вже зазначали, що властивість повноти не виконується при машинному зображенні дійсних чисел, оскількине кожен правильний дріб може бути зображений у вигляді скінченного. Наприклад, 1/9 0.11111..., 1/3 0.333333... і т. д. При роботі з такими числами використовується не точне, а наближене значення. Це потрібно враховувати при написанні програм.
Двійкові дроби теж можуть бути нескінченними. Більше того, не кожне число, що виражається скінченним десятковим дробом, може бути також зображено скінченним двійковим. Наприклад, число 1/5 зображується скінченним десятковим дробом як 0.2, але воно не може бути виражене скінченним двійковим дробом. Такі дроби завжди періодичні, оскільки раціональне число зображується скінченним або періодичним дробом у системі числення з будь-якою основою.
Розглянемо кілька прикладів некоректного використання дійсних типів.
1. Приклад появи зайвих цифр.
#include <string.h>
#include <stdlib.h>
main()
{
float r;
r=0.1;
printf("%.15e",r);
}
Після виконання програми ми побачимо 1.00000001490116e–001. Точність типу float – 7–8 десяткових розрядів, так що результат правильний. Однак звідки взялися інші цифри?
Виявляється, число 0.1 не зображується у вигляді скінченного двійкового дробу, воно дорівнює 0.0(0011). І цей нескінченний двійковий дріб округляється на 24 знаках; ми одержуємо не 0.1, а деяке наближене число. Проекспериментувавши з різними числами, можна помітити, що точно зображуються ті числа, що виражаються у вигляді , де– деякі цілі числа.
2. Приклад незрозумілого результату виконання програми.
#include <string.h>
main()
{
float r;
r=0.1;
if (r==0.1)
printf("дорівнює \n");
else
printf("не дорівнює \n");
}
Після запуску програми ми отримаємо "не дорівнює". Змінна r при присвоюванні одержує значення 0.100000001490116, тому що спочатку число 0.1 перетворюється до типу long double, а потім, при присвоюванні, відбувається округлення до типу float. Процесори Intel працюють із 10-байтним типом long double, тому й ліва, і права частини рівності в умові спочатку перетворюються на цей тип, і лише потім здійснюється порівняння. Число, що зберігається в змінній r (уже не 0.1!), зображується у вигляді скінченного двійкового дробу. При перетворенні цього числа в long double молодші, надлишкові, порівняно з типом float, розряди мантиси, просто заповнюються нулями. Ми одержуємо те саме число, тільки записане у форматі long double.
Число 0.1 із правої частини рівності перетвориться в long double без проміжного перетворення в single і матиме нескінченне зображення у двійковому коді, а деякі з молодших розрядів мантиси міститимуть ненульові значення. Тому порівнюють близькі, але не рівні числа.
Якщо замінити число 0.1 на 0.5, то одержимо "дорівнює", тому що 0.5 – це скінченний двійковий дріб. При прямому зведенні його до типу extended у молодших розрядах будуть нулі. Такі самі нулі виявляються в цих розрядах при перетворенні числа 0.5 типу float у тип long double. Тому в результаті ми порівнюємо два однакових числа.
3. Розглянемо програму:
#include <string.h>
#include <stdlib.h>
main()
{
float r1;
double r2;
r1=0.1;
r2=0.1;
if (r1==r2)
printf("дорівнює");
else
printf("не дорівнює");
}
Одержимо такий самий результат, тобто "не дорівнює", із тих самих причин.
4. Розглянемо програму:
#include <stdio.h>
main()
{
float r;
int i;
r=1;
for(i=1;i<=10;i++)
r-=0.1;
printf("r=%e\n",r);
}
Після виконання програми на екрані з'явиться –7.301569E–008, тому що число 0.1 не може бути передане точно у жодному із дійсних типів. При цьому постійно відбуваються округлення, завдяки яким ми одержуємо в результаті не нуль, а майже нуль.
Коли ми маємо справу з обчисленнями з обмеженою точністю, виникає парадокс. Наприклад, ми маємо точність до трьох значущих цифр. Додамо до числа 1.00 число 1.00*10–4. Ми мали б одержати 1.0001. Однак у нас обмежена точність, тому ми змушені округляти до трьох значущих цифр. У результаті виходить 1.00. Іншими словами, до деякого числа ми додаємо інше, яке більше нуля, і, через обмежену точність, одержуємо те саме число.
Означення 5.2. Найменше додатне число, при додаванні якого до одиниці результат не дорівнює одиниці, називається машинним епсілоном.
Написати програму для обчислення машинного епсілона.
#include <stdio.h>
main()
{
double r;
r=1;
while (1+r/2>1) r=r/2;
printf("%.20e",r);
}
У результаті буде отримане число 1.08420217248550e–19 для комп'ютерів класу Pentium III.