



                          ГЛАВА 14

              Использование сопроцессора 8087

     В Турбо-Паскале вы можете работать с двумя типами чисел
- целыми  (короткими целыми, целыми, длинными целыми, целыми
длиной в  байт, целыми длиной в слово) и  вещественными (ве-
щественными, вещественными одинарной точности, вещественными
двойной точности, повышенной точности, сложными). Веществен-
ные числа называют  также  числами с  плавающей запятой. Для
облегчения работы с целыми числами создан процессор 8086, но
для работы с вещественными числами на этом процессоре затра-
чивается гораздо больше времени и усилий. Для семейства про-
цессоров 8086 предназначено соответствущее семейство вспомо-
гательный  специализированных процессоров для математических
вычислений (сопроцессоров) 8087.
     Процессор 8087 - это специальный процессор для обработ-
ки чисел, который может входить в  состав вашего  компьютера
РС. С помощью него операции с плавающей запятой  выполняются
очень быстро. Поэтому если вы собираетесь использовать боль-
шой объем вычислений с плавающей запятой,  то вам, вероятно,
понадобится сопроцессор.
     Турбо-Паскаль построен таким образом,  что он обеспечи-
вает оптимальное выполнение операций с плавающей запятой не-
зависимо от наличия процессора 8087.

     1.  Для программ, работающих на  компьютере РС, незави-
         симо от  того,  оснащен  он  сопроцессором 8087 или
         нет, в  Турбо-Паскале  предусмотрено  использование
         вещественных  чисел  и  соответствующая  библиотека
         программ, которые предназначены для выполнения опе-
         раций с плавающей запятой. Числа вещественного типа
         занимают 6  байтов памяти.  При этом обеспечивается
         представление  чисел  в диапазоне от 2,9х10**-39 до
         1,7х10**38 с 11-12  значащими  цифрами. Программы в
         библиотеке программ для работы с плавающей запятой
         оптимизированы по скорости и по размеру и использу-

                                - 2 -


         ют самые новейшие средства процессора 8087.
     2.  Если вы пишете программы, использующиеся  только на
         компьютерах, оснащенных  сопроцессором  8087, то вы
         можете указать Турбо-Паскалю на необходимость полу-
         чения  выполняемого  кода,  в  котором используется
         плата процессора 8087. Это даст вам возможность ис-
         пользования четырех дополнительных типов веществен-
         ных чисел (одинарной и двойной точности, повышенной
         точности,  сложного  типа)  и  расширенный диапазон
         представления  чисел  с  плавающей  запятой  -   от
         1,9х10***-4951 до 1,1х10**4943  с  19-20  значащими
         цифрами.

      Примечание: При компиляции с режимом числовой обработ-
      ки, то есть с директивой {$N+},  возвращаемые програм-
      мыми  модуля  Systем  (Sqrt, Рi, Sin и т.д.)  значения
      представляют собой не действительные числа, а  числа с
      повышенной точностью.

       {$N+}

       begin
         Writeln(Pi); { 3.14159265358979E+0000 }
       end.

       {$N-}

       begin
         Writeln(Pi); { 3.1415926536E+00 }
       end.

           Вы можете выбирать одну из двух возможных моделей
      генерации  кода для операций с плавающей запятой.  Это
      делается с помощью директив компилятора $N  или с  по-
      мощью записи О/С/Numeric  из  меню  режимов обработки.
      Директива {$N-}  указывает,  что  операции с плавающей
      запятой  будут выполняться   с   помощью   программных
      средств, а {$N+} - на выполнение этих  операций с  по-

                                - 3 -


      мощью аппаратных средств.

     В оставшейся части данной главы обсуждаются специальные
вопросы, касающиеся использования процессора 8087 в програм-
мах на Турбо-Паскале.

                Типы данных процессора 8087

     В дополнение к вещественному типу для программ, исполь-
зующих средства процессора  8087,  предусматривается  четыре
новых действительных типа:

     1.  Тип с одинарной точностью, представляющий собой на-
         именьший формат, который вы можете использовать для
         чисел  с плавающей запятой. Он занимает 4 байта па-
         мяти  обеспечивает  диапазон представления чисел от
         1,5х10**-45 до 3,4х10**48 с 7-8 значащими цифрами.
     2.  Тип с двойной точностью, занимающий 8 байтов памяти
         и обеспечивающий представление чисел в диапазоне от
         5,0х10**-334 до 1,7х10**308 с 15-16 значащими  циф-
         рами.
     3.  Тип с повышенной точностью представляет собой  наи-
         больший формат представления чисел с плавающей  за-
         пятой, обеспечиваемый процессором 8087. Он занимает
         10 байтов памяти и обеспечивает диапазон  представ-
         ления чисел  от  1,9х10**-4952  до  1,1х10**4932  с
         19-20 значащими цифрами. Любые арифметические  опе-
         рации, в которых участвуют  числа вещественного ти-
         па,  выполняются с точностью и диапазоном представ-
         ления,  соответствующими  типу  с  повышенной  точ-
         ностью.
     4.  Числа сложного типа используются для предварительно
         объединенных значений в 8 байтах памяти,  обеспечи-
         вая при этом диапазон представления от -2**63+1  до
         2**63-1,   что    составляет    приблизительно   от
         -9,2х10**18 до 9,2х10**18. Сложный тип можно  срав-
         нить с длинным целым типом  (двойная точность),  но
         он считается вещественным типом, поскольку при опе-

                                - 4 -


         рациях с числами этого типа используется  сопроцес-
         сор 8087. Сложный тип хорошо подходит для представ-
         ления значений денежных единиц, представляющих  со-
         бой сотни и тысячи, которые используются в приклад-
         ных коммерческих программах.

     Независимо от того, используете вы сопроцессор 8087 или
нет,  6-битовый  вещественный тип является допустимым. Таким
образом,  при переходе к использованию сопроцессора 8087 вам
не потребуется изменять исходный текст программы и вы можете
использовать  файлы  данных,  созданные программами, которые
работают с программно обеспечиваемыми операциями с плавающей
запятой.
     Отметим, однако, что аппаратные вычисления с переменны-
ми вещественного типа выполняются несколько медленнее, чем с
переменными другого типа. Это связано с тем, что сопроцессор
8087 не может непосредственно обрабатывать вещественный фор-
мат. Вместо этого, перед  выполнением операций, для преобра-
зования вещественных значений в числа с повышенной точностью
требуются обращения к библиотечным программам. Если вы заин-
тересованы в максимальной скорости выполнения и не  собирае-
тесь использовать свою программу на системах без сопроцессо-
ра 8087, то возможно вы захотите использовать  одинарный ве-
щественный, вещественный с двойной точностью, вещественный с
повыщенной точностью и сложный типы явным образом.

       Арифметические операции с повышенной точностью

     При использовании сопроцессора 8087  тип  с  повышенной
точностью является основой всех  операций с  плавающей запя-
той. В Турбо-Паскале тип с повышенной точностью используется
для представления  всех  нецелых  числовых констант, а также
при  вычислении  всех  выражений нецелого типа.  Например, в
следующих операциях присваивания все правые части  выражений
будут вычисляться, как выражения с  повышенной  точностью, а
затем их тип будет преобразован к типу соответствующей левой
части:


                                - 5 -


 var
   X, AA, B, C : real;
 begin
   X := (B + Sqrt(B*B - A*C))/A;
 end;

     Турбо-Паскаль выполняет вычисления с точностью и диапа-
зоном представления чисел,  соответствующими типу с повышен-
ной точностью, без  дополнительных усилий программиста.  До-
полнительная точность приводит к меньшим ошибкам округления,
а дополнительный диапазон означает, что ситуации  переполне-
ния и потери значимости будут встречаться в программах реже.
     Вы можете обойтись и без дополнительных  автоматических
возможностей вычислений в повышенной точностью  Турбо-Паска-
ля. Например, вы можете описать  переменные,  использующиеся
для  промежуточных  вычислений,  как переменные с повышенной
точностью.  В следующем примере вычисляется сумма произведе-
ний:

  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;

     Если бы переменная Т  была  описана,  как  переменная с
одинарной точностью, то при каждом цикле операции присваива-
ния для переменной Т были бы выполнены с ошибкой  округления
и ограничениями,  соответствующими  одинарной точности.  Но,
поскольку переменная Т является переменной с повышенной точ-
ностью,  то все ошибки округления (кроме операции, при кото-
рой значение переменной Т присваивается переменной Suм) име-
ют ограничения, соответствующие повышенной точности. Меньшие

                                - 6 -


ошибки округления означают более точный результат.
     Для значений формальных параметров и результата функции
вы  также можете задать повышенную точность. Это поможет из-
бежать ненужных преобразований типов чисел, приводящих к по-
тере точности. Например:

  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 значащих цифр.


                                - 7 -


             Стек вычислений сопроцессора 8087

     У сопроцессора 8087 имеется внутренний стек вычислений,
который может быть глубиной до восьми уровней. Доступ к зна-
чению, находящемуся в стеке сопроцессора 8087 осуществляется
намного быстрее,  чем  доступ к переменной в памяти, поэтому
для достижения  максимально  возможной  производительности в
Турбо-Паскале внутренний стек сопроцессора 8087 используется
для хранения временных результатов и для передачи параметров
процедурам и функциям.
     При  использовании стека сопроцессора 8087 для передачи
параметров подразумевается что  процедура или функция не мо-
жет иметь больше  восьми  значений  параметров с типами, ис-
пользующимися в сопроцессоре 8087 (одинарной точности, двой-
ной  точности,  повышенной точности или сложного типа).  При
попытке описать большее число параметров компилятор не выда-
ет сообщения об ошибке, но во время выполнения программа при
вызове подпрограммы завершит свою работу с ошибкой. На число
параметров вещественного типа, которые вы  можете  использо-
вать, ограничений не накладывается.  Вы можете также описать
любое число параметров var.

      Примечание: В выполняемом  коде  при входе в процедуры
      или функции  любые параметры с типами, использующимися
      в сопроцессоре 8087, сохраняются во временных отведен-
      ных им ячейках.  Параметры могут занимать пространство
      стека  сопроцессора 8087  только во время вызова, а не
      во время выполнения процедуры или функции.

     Теоретически слишком  сложные  выражения  вещественного
типа могут вызвать переполнение стека сопроцессора 8087. Од-
нако этого не может случиться, поскольку для  этого потребо-
валось бы, чтобы в выражении получалось более  восьми проме-
жуточных результатов.
     Более весомая  опасность  таится  во  вложенных вызовах
функций.  Если такие  конструкции составлены некорректно, то
они, вполне  вероятно,  могут  привести к переполнению стека
сопроцессора 8087.

                                - 8 -


     Предположим, что функция Теst является функцией с повы-
шенной с точностью и у  нее  имеется три  параметра-значения
с повышенной точностью. Тогда выражение:

 X := Test(A,B,Test(C,D,Test(E,F,Test(X,Y,Z))));

вызовет переполнение стека сопроцессора 8087. Это произойдет
потому, что при самом внутреннем обращении к  функции Теst в
стек сопроцессора 8087 уже помещено 6  значений  с плавающей
запятой и осталось место только для двух.  Правильной конст-
рукцией в данном случае будет:

 X := Test(X,Y,Z);
 X := Test(E,F,X);
 X := Test(C,D,X);
 X := Test(A,B,X);

     Аналогичная  ситуация может возникнуть в рекурсивно вы-
полняющихся функциях. Рассмотрим, например, следующую проце-
дуру, в которой с помощью рекурсии вычисляются  числа  Фибо-
наччи:

 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 приведет к пе-
реполнению стека сопроцессора 8087, так как значений N боль-
ше, чем 8. Причина заключается в том, что последний оператор
присваивания требует временного сохранения результата выпол-
нения процедуры  Fib(N-1) в стеке сопроцессора 8087.  Каждое
рекурсивное обращение выделяется одна ячейка стека и  на де-
вятом обращении произойдет  переполнение  стека.  Корректной
конструкцией в этом случае будет:


                                - 9 -


 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 конечно тоже может переполниться, но это обычно требует
гораздо большего числа рекурсивных вызовов).

              Запись действительных чисел при
              использовании сопроцессора 8087

     Если была указана директива  {$N+}, то стандартные про-
цедуры Write и Writeln для того,  чтобы обеспечить представ-
ление в расширенном диапазоне, выводят в строке с десятичны-
ми числами с плавающей  запятой  четыре цифры для экспоненты
вместо двух. Аналогично, стандартная процедура Str при выбо-
ре формата с плавающей запятой возвращает значение экспонен-
ты, состоящее из четырех цифр.

      Модули, в которых используется сопроцессор 8087

     Модули, в которых  используется сопроцессор 8087, могут
вызываться другими модулями  или  программами  только в  том
случае, если эти модули или программы  были скомпилированы с
директивой {$N+}.То, что модуль использует сопроцессор 8087,
определяется тем, содержатся ли в нем инструкции сопроцессо-
ра 8087, а не директивой $N во время их компиляции. Это поз-
воляет компилятору быть  более  "снисходительным",  когда вы
случайно компилируете модуль (в котором используется  сопро-

                                - 10 -


цессор 8087), не указав директиву {$N+}.

              Распознавание сопроцессора 8087

     Исполняющая библиотека Турбо-Паскаля, встроенная в вашу
программу  (скомпилированную  с директивой {$N+}) включает в
себя код инициализации, который автоматически распознает на-
личие в системе микросхемы сопроцессора 8087. Если сопроцес-
сор 8087 имеется, то программа будет его  автоматически  ис-
пользовать.  В случае  же его отсутствия программа будет ис-
пользовать эмулирующую библиотеку исполняющей системы.  Если
программа  компилировалась с директивой {$E-} и по время на-
чала ее работы сопроцессор не обнаруживается,  то  программа
завершает  работу  с сообщением Numeric coprocessor required
(требуется сопроцессор арифметических вычислений).
     Есть несколько случаев, когда вы возможно захотите  из-
менить  такую  принятую  по умолчанию логику автообнаружения
сопроцессора. Например, в вашей системе может присутствовать
сопроцессор  8087, но вы захотите проверить, как будет рабо-
тать программа, предназначенная для функционирования на сис-
темах  без  сопроцессора.  Или же потребуется запустить вашу
программу на системе, совместимой с компьютером  РС,  но  на
этой  системе при работе алгоритма автообнаружения будет вы-
водиться некорректная информация (например, будет сообщаться
о наличие сопроцессора, когда на самом деле его нет, или на-
оборот).
     В Турбо-Паскале предусмотрена возможность отмены приня-
той  по  умолчанию логики автоматического распознавания. Эта
возможность задается переменной операционной среды 87.
     Вы можете установить переменную операционной среды 87 в
ответ на подсказку ДОС с помощью команды SET, например, сле-
дующим образом:

     SET 87=Y

или

     SET 87=N

                                - 11 -



     Установка для переменной операционной среды 87 значения
N  (нет)  указывает коду инициализации, что вы не хотите ис-
пользовать сопроцессор 8087, хотя он может и присустствовать
в  системе. И наоборот: установка для переменной 87 значения
Y (да) означает, что сопроцессор имеется и вы хотите,  чтобы
ваша  программа его использовала. Однако при этом нужно пом-
нить о том, что установка для переменной 87 значения  Y  при
отсутствии  в системе сопроцессора 8087 приведет к тому, что
ваша программа аварийно завершит работу или зависнет.
     Если переменная операционной среды 87 определена, а  вы
хотите,  чтобы  она  стала неопределенной, то можно ввести в
ответ на подсказку ДОС:

     SET 87=

и нажать клавишу Enter.
     Если в операционной среде ДОС присутствует запись 87=Y,
или  если  код инициализации успешно распознает сопроцессор,
то далее код инициализации выполняет  последующие  проверки,
чтобы  определить,  какой  это  сопроцессор (8087, 80287 или
80387). Это необходимо для  того,  чтобы  Турбо-Паскаль  мог
корректно  работать  с отдельными несовместимостями, которые
имеются между сопроцессорами различных типов.
     Резальтат автоматического распознавания наличия  сопро-
цессора  и его модели сохраняется в переменной Test8087 (ко-
торая описывается в модуле System). Для нее определены  сле-
дующие значения:

     0 - сопроцессор не обнаружен;
     1 - обнаружен сопроцессор 8087;
     2 - обнаружен сопроцессор 80287;
     3 - обнаружен сопроцессор 80387.

     Чтобы определить характеристике системы, на которой ра-
ботает  раша  программа, вы можете в программе проверить со-
держимое переменной Test8087. В  частности,  эту  переменную
можно  проанализировать для того, чтобы определить, эмулиру-

                                - 12 -


ются инструкции работы с плавающей запятой, или они действи-
тельно выполняются.

                  Использование эмуляции
           сопроцессора 8087 на языке ассемблера

     Когда компоновка объектных файлов выполняется с  дирек-
тивой {$L имя_файла}, необходимо обеспечить, чтобы эти файлы
компилировалить с разрешением  эмуляции  сопроцессора  8087.
Например,  если  вы используете инструкции сопроцессора 8087
во внешних процедурах на языке ассемблера,  необходимо  убе-
диться,  что  при  ассемблировании  файлов .ASM в файлы .OBJ
эмуляция разрешена. В противном случае инструкции  сопроцес-
сора 8087 не могут эмулироваться на машинах без сопроцессора
8087. Для разрешения эмуляции используйте переключатель  ко-
мандной строки Турбо-Ассемблера /Е.


