Застосування співпроцесора значно збільшує точність математичних розрахунків і прискорює їх виконання. Справа лише в малому - наявності його і умінні використовувати.
Щоб програма могла задіяти можливості співпроцесора, вона повинна в своєму початку мати директиву (ключ режиму компілятора) $ N +:
Режим компіляції $ N + є глобальним і не може змінюватися надалі. При створенні модулів (UNIT), орієнтованих на роботу з співпроцесором, тобто використовують вводяться ним типи, вказівка ключа $ N + в них необов'язково. Важливо лише, щоб він був в головній програмі, що включає в себе ці модулі.
При необхідності відключити співпроцесор повинен вказуватися ключ $ N-. При цьому програма може перестати компілюватиметься (компілятор «забуде» типи чисел з підвищеною точністю).
Якщо в ПЕОМ встановлено співпроцесор, то компілятор сам визначає ключове слово CPU87 для умовної компіляції. Це можна використовувати для автоматичного вибору режиму компіляції:
Наведена вище конструкція визначає, як буде скомпільовано текст - в розрахунку на співпроцесор або без нього.
Після компіляції виконання програми щоразу починається з перевірки наявності співпроцесора і визначення його типу. Результат перевірки записується в зумовлену змінну системної бібліотеки - Test8087 типу Byte (табл. 9.8).
Значення Test8087 Підключений 8087 2 Підключений 80287 3 Підключений 80387
Якщо програма компілювати в режимі, а значення Test8087 вийшло рівним 0, то програма зупиниться з видачею повідомлення про необхідність співпроцесора. Існує, однак, засіб відключити автоматичну перевірку наявності співпроцесора при запуску програми. Треба ввести системну змінну MS-DOS з ім'ям 87 і значеннями Y (від YES - так) і N (від NO - немає). Найкраще це зробити в файлі AUTOEXEC.BAT, вставивши рядок
Значення системної змінної MS-DOS 87, рівне Y, накаже вважати співпроцесор підключеним, а N - відповідно відключеним. Взагалі кажучи, краще не обманювати техніку і програми. Прибережіть цю методику (з SET 87 =) на самий крайній випадок.
Турбо Паскаль дає можливість емулювати роботу співпроцесора програмним шляхом. Це означає, що можна створити програму, яка буде працювати з високою точністю незалежно від наявності співпроцесора. Виявлено співпроцесор - добре, він і буде навантажений, немає співпроцесора - вся точність буде отримана імітацією його. Зрозуміло, що в останньому випадку будуть втрати в часі рахунку, і чималі. Включенням емуляції управляє ключ $ Е. Він має сенс тільки поруч з ключем $ N. Можливі такі їх поєднання:
- підключення бібліотеки для емуляції співпроцесора; при його відсутності точність забезпечується програмно за рахунок швидкості; - програма зможе працювати тільки на машинах з співпроцесором;
і - співпроцесор не використовується, ключ емуляції ігнорується. Програма працює тільки зі звичайною точністю і швидкістю.
Якщо в програму вставлений зовнішній код директивою, то для роботи з співпроцесором цей код повинен бути отриманий з урахуванням використання інструкцій 80X87.
Переваги від використання співпроцесора - це, в першу чергу, швидкість обчислень, яка може вирости в кілька разів. Друга перевага - збільшення точності обчислень з плаваючою точкою. У розрахунку на математичний співпроцесор вводяться типи, наведені в табл. 9.9.
Всі ці типи - речові, за винятком Comp, який є «дуже довгим» цілим типом (зберігає тільки цілі значення). Діапазон цього типу в таблиці заданий округлено, так як реальні числа (від -2 до 263-1) занадто довгі.
Звичайний тип Real (6 байт, діапазон 2.9Е-39. 1.7Е + 38, 11-12 значущих цифр) буде працювати з співпроцесором, але вкрай неефективно. Цей формат - чужий для співпроцесора, і час, «з'їдається» перетворенням його в сопроцессорний тип, перекриває прискорення. А точно не додається. Тому краще всього ввести свій тип, наприклад Float, і розуміти під ним або Real, або чисто сопроцессорний дійсний тип в залежності від режиму компіляції.
d. Array [1..9] of Float;
Цілі типи Турбо Паскаля працюють з співпроцесором без будь-яких застережень.
Особливо важливим є питання точності обчислень. При використанні співпроцесора всі стандартні математичні оператори й функції мови, які повертають зазвичай значення Real, починають повертати значення типу Extended. У зв'язку з цим має сенс спиратися саме на цей тип як базовий. Проте цілком можливо, що в програмі братимуть участь змінні різних типів. У таких випадках при необхідності буде проводитися перетворення значень, а значить, втрата точності. При обчисленні значень правих частин операторів присвоювання результат має точність, збігається з найбільш точним з типів членів виразу (або, що те ж саме, з найбільш ємним типом). Це означає, що в присвоєнні
значення виразу справа буде обчислено як тип Extended. Але при присвоєнні його змінної result «малого» типу Single буде вироблено усічення, і різко зменшиться число значущих цифр після десяткової точки. Подібні ситуації треба передбачити і намагатися уникати їх. Особливо неприємні вони в циклах підсумовування:
for i: = 32767 to 65535 do Sum: = Sum + i / e;
Тут подібні втрати будуть повторені тисячі разів, і накопичена помилка може бути порівнянна з самої сумою. Виправити ситуацію легко: треба ввести додаткову змінну eSum точного типу Extended для суматора, і переписати цикл:
for i: = 32767 to 65535 do eSum: = eSum + i / e;
Тепер втрати будуть значно менше.
З тієї ж причини (через усічення точності) некоректною є операція порівняння двох різнотипних речових змінних або змінної з виразом (останнє, як вже зазначалося, може бути обчислено в типі Extended). Так, порівняння в прикладі:
при формальної правильності та очевидності дасть результат False - помилково, так як d має менше значущих цифр, ніж e. Зазвичай при порівнянні речових значень перевіряють їх збіг, а ступінь розбіжності. Якщо ця ступінь порівнянна з точністю уявлення найбільш грубого числа, то значення можна вважати рівними. Так, умова if в останньому прикладі було б переписати так:
if Abs (d-e) <1.0Е-15 then.
Тут 1.0Е-15 - точність для типу змінної d (Double).
Продовжимо перелік особливостей застосування співпроцесора. Його наявність в ПЕОМ та використання сильно впливає на роботу функції округлення Round: вона починає округляти полуторні значення в бік найближчого парного цілого числа (це називається «банківським способом»)! наприклад:
без співпроцесора з співпроцесором
Round (0.5) -> 1 Round (0.5) -> 0
Round (1.5) -> 2 Round (1.5) -> 2
Round (2.5) -> 3 Round (2.5) -> 2
Round (3.5) -> 4 Round (3.5) -> 4
З іншими значеннями (без '.5') функція працює нормально.
Деякі неприємності можуть чекати любителів рекурсивного підходу до написання функції. Можливі, в принципі, ситуації, коли рекурсивні виклики переповнять внутрішній стек даних співпроцесора, розрахований на вісім рівнів рекурсії, і виникне збій програми. Можливим рішенням буде рознесення сложнорекурсівних виразів типу Fn: = Fn (N-1) + Fn (N-2) по локальних змінних, наприклад, f1: = Fn (N-1); і f2: = Fn (N-2). Після цього вираз Fn: = f1 + f2 буде безпечним для співпроцесора.
Завершуючи тему використання співпроцесора, нагадаємо, що і розширені речові типи, і тип Real при роботі з співпроцесором 80X87 виводяться на друк операторами Write і WriteLn з 4 цифрами в показнику ступеня:
при $ N- WriteLn (123.4) видасть 1.2340000000Е + 02,
але при $ N + WriteLn (123.4) видасть 1.234000000000000Е + 0002.
Цей факт треба враховувати при форматованому виведення і при перетворенні чисел в рядок процедурою Str.