Глава 15. Использование сопроцессора 80x87
В Borland Pascal вы можете работать с двумя типами чисел -
целыми (короткими целыми - Shortint, целыми - Integer, длинными
целыми - Longint, целыми длиной в байт - Byte, целыми длиной в
слово - Word) и вещественными (вещественными - Real, вещественны-
ми одинарной точности - Single, вещественными двойной точности -
Double, повышенной точности - Extended, сложными - Comp). Вещест-
венные числа называют также числами с плавающей точкой (плавающей
запятой). Для облегчения работы с целыми числами создан процессор
8086, но для работы с вещественными числами на этом процессоре
затрачивается гораздо больше времени и усилий. Для семейства про-
цессоров 8086 предназначено соответствующее семейство вспомога-
тельных специализированных процессоров для математических вычис-
лений (сопроцессоров) 80x87.
Процессор 80x87 - это специальный сопроцессор для обработки
чисел, который может входить в состав вашего компьютера РС. С по-
мощью него операции с плавающей точкой выполняются очень быстро.
Поэтому если вы собираетесь использовать большой объем вычислений
с плавающей точкой, то вам, вероятно, понадобится сопроцессор.
Borland Pascal построен таким образом, что он обеспечивает
оптимальное выполнение операций с плавающей точкой независимо от
наличия сопроцессора 80x87.
* Для программ, работающих на компьютере РС, независимо от
того, оснащен он сопроцессором 80x87 или нет, в Borland
Pascal предусмотрено использование вещественных чисел и
соответствующая библиотека программ, которые предназначены
для выполнения операций с плавающей точкой. Числа вещест-
венного типа занимают 6 байт памяти. При этом обеспечива-
ется представление чисел в диапазоне от 2.9х10^-39 до
1.7х10^38 с 11-12 значащими цифрами. Программы в библиоте-
ке программ для работы с плавающей точкой оптимизированы
по скорости и по размеру и используют самые новейшие
средства процессора 80x87.
* Если вы пишете программы, использующиеся только на компь-
ютерах, оснащенных сопроцессором 80x87, то вы можете ука-
зать Borland Pascal на необходимость получения выполняемо-
го кода, в котором используется плата процессора 80x87.
Это даст вам возможность использования четырех дополни-
тельных типов вещественных чисел (одинарной и двойной точ-
ности, повышенной точности, сложного типа) и расширенный
диапазон представления чисел с плавающей точкой - от
1.9х10^-4951 до 1,1х10^4943 с 19-20 значащими цифрами.
С помощью директивы компилятора $N или параметра меню
Options¦Cоmpiler (Параметры¦Компилятор) 80x87/80287 можно перек-
лючаться между различными моделями генерации кода с плавающей
точкой. По умолчанию используется состояние {$N-}. В этом состоя-
нии компилятор использует 6-байтовую библиотеку с плавающей точ-
кой, что позволяет вам работать только с переменными типа Real. В
состоянии {$N+} компилятор генерирует код для сопроцессора 80x87,
что дает вам дополнительную точность и доступ к 4 дополнительным
вещественным типам.
В Windows при компиляции с режимом числовой обработки, то
есть с директивой {$N+}, убедитесь, что в вашей системе можно
найти библиотеку эмуляции Windows 8087 - WIN87EM.DLL. Эта библио-
тека обеспечивает необходимый интерфейс между сопроцессором
80х87, Windows и вашей прикладной программой. Если сопроцессор
80х87 в вашей системе отсутствует, то библиотека WIN87EM.DLL бу-
дет эмулировать его программно. Эмуляция существенно замедляет
работу по сравнению с реальным сопроцессором 80х87, но обеспечи-
вает выполнение вашей прикладной программы на любой машине.
В реальном или защищенном режиме DOS, даже если у вас нет
сопроцессора 8087, вы можете указать Borland Pascal, что нужно
включить библиотеку исполняющей системы, которая эмулирует ариф-
метический сопроцессор 8087. В случае наличия сопроцессора 8087
он используется. Если сопроцессор отсутствует, его работа эмули-
руется библиотекой исполняющей системы (за счет некоторой потери
скорости работы программы).
Для разрешения и запрещения эмуляции сопроцессора 8087 ис-
пользуются директива компилятора $E и параметр Emulation (Эмуля-
ция) меню Options¦Compiler (Параметры¦Компилятор). По умолчанию
используется состояние {$E+}. В этом состоянии в программу авто-
матически включается полная эмуляция сопроцессора 8087. В состоя-
нии {$E-} используется существенно меньшая часть библиотеки с
плавающей точкой, а полученный в результате файл .EXE будет рабо-
тать только на машинах с сопроцессором 8087.
В приложении Windows директива компилятора $E не действует.
Не действует она также в модуле. Более того, если программа ком-
пилировалась с директивой {$N-}, а все модули программы компили-
ровались с директивой {$N+}, то библиотека исполняющей системы
для сопроцессора 8087 не требуется, и директива компилятора $E
игнорируется.
Прикладной программе Windows не требуется библиотека испол-
няющей системы 80x87. Вместо этого ей нужно поддерживающая библи-
отека WIN87EM.DLL, поставляемая с Windows, которая обеспечивает
необходимый интерфейс между вашей прикладной программой, Windows
и сопроцессором. Таким образом, в Windows даже при наличии в ва-
шей системе сопроцессора 80х87 для выполнения программ, скомпили-
рованных в состоянии {$N+}, должна присутствовать библиотека эму-
ляции WIN87EM.DLL (данная библиотека - это часть Windows, а не
Borland Pascal). При отсутствии сопроцессора WIN87EM.DLL будет
эмулировать его операции программным путем, что замедляет выпол-
нение программы и не гарантирует, что использующая сопроцессор
80x87 программа сможет работать на любой машине.
Когда вы запускаете прикладную программу Windows, cкомпили-
рованную в состоянии {$N+}, убедитесь, что она может найти в сис-
теме файл WIN87EM.DLL.
Когда вы выполняете компиляцию в режиме кода 80х87 (директи-
ва {$N+}), то возвращаемые подпрограммы модуля Systем (Sqrt, Рi,
Sin и т.д.) значения представляют собой не вещественные числа, а
числа типа Extended (с повышенной точностью).
{$N+}
begin
Writeln(Pi); { 3.14159265358979E+0000 }
end.
{$N-}
begin
Writeln(Pi); { 3.1415926536E+00 }
end.
В оставшейся части данной главы обсуждаются специальные воп-
росы, касающиеся использования процессора 80x87 в программах
Borland Pascal.
Типы данных процессора 80x87
В дополнение к вещественному типу для программ, использующих
средства процессора 80x87, предусматривается четыре новых вещест-
венного типа:
1. Тип с одинарной точностью Single, представляющий собой
наименьший формат, который вы можете использовать для
чисел с плавающей точкой. Он занимает 4 байта памяти
обеспечивает диапазон представления чисел от 1.5х10^-45
до 3.4х10^48 с 7-8 значащими цифрами.
2. Тип с двойной точностью Double, занимающий 8 байт памяти
и обеспечивающий представление чисел в диапазоне от
5.0х10^-334 до 1.7х10^308 с 15-16 значащими цифрами.
3. Тип с повышенной точностью Extended представляет собой
наибольший формат представления чисел с плавающей запя-
той, обеспечиваемый процессором 8087. Он занимает 10
байт памяти и обеспечивает диапазон представления чисел
от 1.9х10^-4952 до 1.1х10^4932 с 19-20 значащими цифра-
ми. Любые арифметические операции, в которых участвуют
числа вещественного типа, выполняются с точностью и диа-
пазоном представления, соответствующими типу с повышен-
ной точностью.
4. Числа сложного типа Comp используются для предварительно
объединенных значений в 8 байтах памяти, обеспечивая при
этом диапазон представления от -2^63+1 до 2^63-1, что
составляет приблизительно от -9.2х10^18 до 9.2х10^18.
Сложный тип можно сравнить с длинным целым типом (двой-
ная точность), но он считается вещественным типом, пос-
кольку при операциях с числами этого типа используется
сопроцессор 8087. Сложный тип хорошо подходит для предс-
тавления значений денежных единиц, представляющих собой
сотни и тысячи, которые используются в прикладных ком-
мерческих программах.
Независимо от того, используете вы сопроцессор 80x87 или
нет, 6-битовый вещественный тип является допустимым. Таким обра-
зом, при переходе к использованию сопроцессора 80 x87 вам не пот-
ребуется изменять исходный текст программы, и вы можете использо-
вать файлы данных, созданные программами, которые работают с
программно обеспечиваемыми операциями с плавающей точкой.
Отметим, однако, что аппаратные вычисления с переменными ве-
щественного типа выполняются несколько медленнее, чем с перемен-
ными другого типа. Это связано с тем, что сопроцессор 80x87 не
может непосредственно обрабатывать вещественный формат. Вместо
этого, перед выполнением операций, для преобразования веществен-
ных значений в числа с повышенной точностью требуются обращения к
библиотечным программам. Если вы заинтересованы в максимальной
скорости выполнения и не собираетесь использовать свою программу
на системах без сопроцессора 80x87, то возможно вы захотите ис-
пользовать вещественный тип с одинарной точностью, вещественный
тип с двойной точностью, вещественный тип с повышенной точностью
и сложный типы явным образом.
Арифметические операции с повышенной точностью
При использовании сопроцессора 80x87 тип с повышенной точ-
ностью Extended является основой всех операций с плавающей точ-
кой. В Турбо Паскале тип с повышенной точностью используется для
представления всех нецелых числовых констант, а также при вычис-
лении всех выражений нецелого типа. Например, в следующих опера-
циях присваивания все правые части выражений будут вычисляться,
как выражения с повышенной точностью, а затем их тип будет преоб-
разован к типу соответствующей левой части:
{$N+}
var
X, AA, B, C : real;
begin
X := (B + Sqrt(B*B - A*C))/A;
end;
Borland Pascal выполняет вычисления с точностью и диапазоном
представления чисел, соответствующими типу с повышенной точ-
ностью, без дополнительных усилий программиста. Дополнительная
точность приводит к меньшим ошибкам округления, а дополнительный
диапазон означает, что ситуации переполнения и потери значимости
будут встречаться в программах реже.
Вы можете обойтись и без дополнительных автоматических воз-
можностей вычислений с повышенной точностью Borland Pascal. Нап-
ример, описать переменные, использующиеся для промежуточных вы-
числений, как переменные с повышенной точностью. В следующем при-
мере вычисляется сумма произведений:
var
Sm : single;
X,Y array[1..100] of single;
I : integer;
T : extended; { для промежуточных результатов }
begin
T := 0.0;
for I := 1 to 100 do T := T + X[I] * Y[I]
Sum := T;
end;
Если бы переменная T была описана, как переменная с одинар-
ной точностью, то при каждом цикле операции присваивания для пе-
ременной T были бы выполнены с ошибкой округления и ограничения-
ми, соответствующими одинарной точности. Но, поскольку переменная
T является переменной с повышенной точностью, то все ошибки ок-
ругления (кроме операции, при которой значение переменной T прис-
ваивается переменной Suм) имеют ограничения, соответствующие по-
вышенной точности. Меньшие ошибки округления означают более точ-
ный результат.
Для значений формальных параметров и результата функции вы
также можете задать повышенную точность. Это поможет избежать не-
нужных преобразований типов чисел, приводящих к потере точности.
Например:
function Area(Radius: extended): extended;
begin
Area := Pi * Radius * Radius;
end;
Сравнение вещественных чисел
Поскольку значения вещественного типа являются приблизитель-
ными, результат сравнения значений различного вещественного типа
не всегда можно предсказать. Например, если Х - переменная ве-
щественного типа с одинарной точностью, а Y - переменная вещест-
венного типа с двойной точностью, то результатом выполнения сле-
дующих операторов будет значение False:
X := 1/3;
Y := 1/3;
Writeln(X = Y);
Причина этого состоит в том, что Х имеет точность только до
7-8 цифр, а Y - точность до 15-16 цифр, и когда оба значения пре-
образуются к типу с повышенной точностью, то после первых 7-8
цифр остальные цифры будут различаться. Аналогично, результатом
выполнения операторов:
X := 1/3;
Writeln(X = 1/3);
будет значение False, результат 1/3 в операторе Writeln вычисля-
ется с точностью до 20 значащих цифр.
Стек вычислений сопроцессора 80x87
У сопроцессора 80x87 имеется внутренний стек вычислений, ко-
торый может быть глубиной до восьми уровней. Доступ к значению,
находящемуся в стеке сопроцессора 80x87 осуществляется намного
быстрее, чем доступ к переменной в памяти, поэтому для достижения
максимально возможной производительности в Borland Pascal внут-
ренний стек сопроцессора 80x87 используется для хранения времен-
ных результатов и для передачи параметров процедурам и функциям.
Теоретически, слишком сложные выражения вещественного типа
могут вызвать переполнение стека сопроцессора 80x87. Однако этого
не может случиться, поскольку для этого потребовалось бы, чтобы в
выражении получалось более восьми промежуточных результатов.
Более весомая опасность таится во вложенных вызовах функций.
Если такие конструкции составлены некорректно, то они, вполне ве-
роятно, могут привести к переполнению стека сопроцессора 80x87.
Рассмотрим, следующую функцию, в которой с помощью рекурсии
вычисляются числа Фибоначчи:
function Fib(N: integer): extended;
begin
if N = 0 then
Fib := 0.0
else
if N = 1 then
Fib := 1.0
else
Fib := Fib(N-1) + Fib(N-2);
end;
Обращение к данной версии процедуры Fib приведет к перепол-
нению стека сопроцессора 80x87, так как значений N больше, чем 8.
Причина заключается в том, что последний оператор присваивания
требует временного сохранения результата выполнения процедуры Fib
(N-1) в стеке сопроцессора 80x87. Каждое рекурсивное обращение
выделяется одна ячейка стека и на девятом обращении произойдет
переполнение стека. Корректной конструкцией в этом случае будет:
function Fib(N : integer) : extended;
var
F1,F2 : Extended;
begin
if N = 0 then
Fib := 0.0
else
if N = 1
then Fib := 1.0
else
begin
F1 := Fib(N-1); F2 := Fib(N-2);
Fib := F1 + F2;
end;
end;
Временные результаты теперь сохраняются в переменных, для
которых отводится стек процессора 8086. (Стек процессора 8086 ко-
нечно тоже может переполниться, но это обычно требует гораздо
большего числа рекурсивных вызовов).
Запись вещественных чисел при использовании сопроцессора 80x87
Если была указана директива {$N+}, то стандартные процедуры
Write и Writeln, чтобы обеспечить представление в расширенном ди-
апазоне, выводят в строке с десятичными числами с плавающей точ-
кой четыре цифры для показателя степени вместо двух. Аналогично,
стандартная процедура Str при выборе формата с плавающей точкой
возвращает значение показателя степени, состоящее из четырех
цифр.
Модули, в которых используется сопроцессор 80x87
Модули, в которых используется сопроцессор 80x87, могут вы-
зываться другими модулями или программами только в том случае,
если эти модули или программы были скомпилированы с директивой
{$N+}. То, что модуль использует сопроцессор 80x87, определяется
наличием в нем инструкций сопроцессора 80x87, а не директивой $N
во время компиляции. Это позволяет компилятору быть более "снис-
ходительным", когда вы случайно компилируете модуль (в котором
используется сопроцессор 80x87), не указав директиву {$N+}.
Когда вы выполняете компиляцию в режиме кода 80х87 (директи-
ва {$N+}), то возвращаемые подпрограммами модуля Systем (Sqrt,
Рi, Sin и т.д.) значения представляют собой не вещественные чис-
ла, а числа типа Extended (с повышенной точностью).
Распознавание сопроцессора 80х87 в программах DOS
Исполняющая библиотека Borland Pascal, встроенная в вашу
программу (скомпилированную с директивой {$N+}) включает в себя
код инициализации, который автоматически распознает наличие в
системе микросхемы сопроцессора 8087. Если сопроцессор 8087 име-
ется, то программа будет его автоматически использовать. В случае
же его отсутствия программа будет использовать эмулирующую библи-
отеку исполняющей системы. Если программа компилировалась с ди-
рективой {$E-} и по время начала ее работы сопроцессор не обнару-
живается, то программа завершает работу с сообщением Numeric
coprocessor required ("Требуется сопроцессор арифметических вы-
числений").
Есть несколько случаев, когда вы возможно захотите изменить
такую принятую по умолчанию логику автоматического обнаружения
сопроцессора. Например, в вашей системе может присутствовать соп-
роцессор 8087, но вы захотите проверить, как будет работать прог-
рамма, предназначенная для функционирования на системах без соп-
роцессора. Или же потребуется запустить вашу программу на
системе, совместимой с компьютером РС, но на этой системе при ра-
боте алгоритма автообнаружения будет выводиться некорректная ин-
формация (например, будет сообщаться о наличие сопроцессора, ког-
да на самом деле его нет, или наоборот).
В Borland Pascal предусмотрена возможность отмены принятой
по умолчанию логики автоматического распознавания. Эта возмож-
ность задается переменной операционной среды 87.
Вы можете установить переменную операционной среды 87 в от-
вет на подсказку DOS с помощью команды SET, например, следующим
образом:
SET 87=Y
или
SET 87=N
Установка для переменной операционной среды 87 значения N
(Нет) указывает коду инициализации, что вы не хотите использовать
сопроцессор 8087, хотя он может и присутствовать в системе. И на-
оборот: установка для переменной 87 значения Y (Да) означает, что
сопроцессор имеется, и вы хотите, чтобы ваша программа его ис-
пользовала. Однако при этом нужно помнить о том, что установка
для переменной 87 значения Y при отсутствии в системе сопроцессо-
ра 8087 приведет к тому, что ваша программа аварийно завершит ра-
боту или "зависнет".
Если переменная операционной среды 87 определена, а вы хоти-
те, чтобы она стала неопределенной, то можно ввести в ответ на
подсказку DOS:
SET 87=
и нажать клавишу Enter.
Если в операционной среде DOS присутствует запись 87=Y, или
если код инициализации успешно распознает сопроцессор, то далее
код инициализации выполняет последующие проверки, чтобы опреде-
лить, какой это сопроцессор (8087, 80287 или 80387). Это необхо-
димо для того, чтобы Турбо Паскаль мог корректно работать с от-
дельными несовместимостями, которые имеются между сопроцессорами
различных типов.
Результат автоматического распознавания наличия сопроцессора
и его модели сохраняется в переменной Test8087 (которая описыва-
ется в модуле System). Для нее определены следующие значения:
---------------T--------------------------------
¦ Значение ¦ Определение ¦
+--------------+--------------------------------+
¦ 0 ¦ сопроцессор не обнаружен ¦
¦ 1 ¦ обнаружен сопроцессор 8087 ¦
¦ 2 ¦ обнаружен сопроцессор 80287 ¦
¦ 3 ¦ обнаружен сопроцессор 80387 ¦
L--------------+---------------------------------
Чтобы определить характеристики системы, на которой работает
ваша программа, вы можете в программе проверить содержимое пере-
менной Test8087. В частности, эту переменную можно проанализиро-
вать для того, чтобы определить, эмулируются инструкции работы с
плавающей точкой, или они действительно выполняются.
Распознавание сопроцессора 80x87 в программе Windows
Операционная среда Windows и библиотека эмуляции WIN87EM.DLL
автоматически распознает наличие в системе платы сопроцессора
80x87. Если сопроцессор 80x87 имеется, то программа будет его ав-
томатически использовать. В случае же его отсутствия программа
будет использовать эмуляцию с помощью WIn87EM.DLL. Чтобы опреде-
лить наличие в системе сопроцессора 80х87, вы можете использовать
функцию GetWinFlags (которая определена в модуле WinProcs) и би-
товую маску wf_80x87 (определенную в модуле WinTypes). Например:
if GetWinFlags and wf_80x87 <> 0 then
Writeln('80x87 присутствует') else
Writeln('80x87 отсутствует');
Использование эмуляции сопроцессора 80x87 на языке ассемблера
Когда компоновка объектных файлов выполняется с директивой
{$L имя_файла}, необходимо обеспечить, чтобы эти файлы компилиро-
вать с разрешением эмуляции сопроцессора 80x87. Например, если вы
используете инструкции сопроцессора 80x87 во внешних процедурах
на языке ассемблера, необходимо убедиться, что при ассемблирова-
нии файлов .ASM в файлы .OBJ эмуляция разрешена. В противном слу-
чае инструкции сопроцессора 80x87 не могут эмулироваться на маши-
нах без сопроцессора 80x87. Для разрешения эмуляции используйте
параметр командной строки Турбо Ассемблера /E.
Назад | Содержание | Вперед