                             ГЛАВА 14.

                        ИСПОЛЬЗОВАНИЕ 8087.


     Существует два вида чисел,  с которыми вы  можете  работать  в
Turbo Pascal:  целые (Shortint,  Integer,  LongInt,  Byte,  Word) и
вещественные (Real,  Single,  Double, Extended, Comp). Вещественные
также известны,  как  числа  с  плавающей  точкой.  Процессор  8086
спроектирован  так,  что легко обрабатывает целые числа,  но тратит
значительно больше времени  и  усилий  на  обработку  вещественных.
Чтобы  улучшить  производительность  обработки  вещественных чисел,
существует математический сопроцессор 8087.
     Сопроцессор 8087   -   это   специальный  аппаратный  числовой
процессор,  который  может  быть  установлен  в  Вашу  машину.   Он
выполняет  операции  с  плавающей  точкой  очень  быстро,  если  вы
используете  много  таких  вычислений,  вам,  вероятно,   необходим
сопроцессор.
     Turbo Pascal  обеспечивает  оптимальную  производительность  с
плавающей точкой в зависимости от того, есть у вас 8087 или нет.

     - Для  программ,  работающих  на  любых  ЭВМ (с 8087 или без),
Turbo Pascal обеспечивает тип real и библиотеку  программ,  которая
обрабатывает вещественные операции. Тип real занимает 6 байт памяти
и обеспечивает диапазон от 2.9x(10** -39) до 1.7x(10**38) с  11-ю -
12-ю  значащими цифрами.  Программная библиотека с плавающей точкой
оптимизирована по  скорости  и  размеру,  приближаясь  в  некоторых
применениях r возможностям, обеспечиваемым 8087.

     - Если  вам требуется дополнительная точность и гибкость 8087,
вы можете инструктировать Turbo Pascal,  генерировать код,  который
использует  8087.  Это  дает  вам  доступ  к четырем дополнительным
вещественным типам (Single,  Double,  Extended и Comp) и  расширяет
диапазон от   3.4x(10**-4951)  до  1.1x(10**4932)  с  19-ю  -  20-ю
значащими цифрами.

     Вы переключаете между двумя различными  моделями  вещественных
чисел,  используя  директиву компилятора $N или кнопку 8087/80287 в
диалоговом окне Options/Compiler.  Значения по умолчанию - {$N-}, и
в  этом  состоянии  компилятор использует 6-и байтовую вещественную
библиотеку, позволяя вам работать только с переменными типа Real. В
состоянии {$N+} компилятор генерирует код для 8087, обеспечивая Вам
повышенную точность и доступ к четырем  дополнительным вещественным
типам.

     Примечание: Когда   программа   компилируется   в  режиме  N+,
значения, возвращаемые  вещественными  программами  модуля   System
(Sqrt, Pi, Sin и т.д.) будет типа Extended вместо Real:

     {$N+}
     begin
        WriteLn(Pi);  {3.14159265358979}
     end.

     {$N-}
     begin
        WriteLn(Pi);  {3.1415926536}
     end.

     Даже если  на  Вашей  ЭВМ  нет  8087,  вы можете указать Turbo
Pascal подключить библиотеку, которая эмулирует сопроцессор. В этом
случае, если 8087 существует - он используется. Если не существует,
он эмулируется программной библиотекой,  что значительно медленнее.
     Директива компилятора  $E и кнопка Emulation в диалоговом окне
Options/Compiler  используется  для   разрешения   или   запрещения
эмуляции 8087.  Значение  по  умолчанию  - $Е+.  И в этом состоянии
полный  эмулятор  8087  автоматически  подключается  к   программе,
которая использует  8087.  В состоянии $E- используется значительно
меньшая библиотека и результирующий .EXE файл может работать только
на ЭВМ с 8087.

     Примечание: Директива  $E  не  имеет  эффекта,  если  стоит  в
модуле,   она   применяется   только   при   компиляции   программ.
Следовательно,  если  программа откомпилирована в состоянии {$N-} и
все модули, используемые программой, также откомпилированы с {$N-},
то библиотека 8087 не подключается и директива $E игнорируется.
     Далее в этой главе обсуждаются специальные вопросы, касающиеся
программ на Turbo Pascal, которые используют 8087.


                         Типы данных 8087.

     Для программ,   которые   используют   8087,   Turbo    Pascal
обеспечивает четыре вещественных типа в дополнение к типу Rreal.

     - Тип   Single   имеет   наименьшую   длину,   которую   можно
использовать для вещественного числа.  Он занимает 4 байта  памяти,
обеспечивая диапазон  от 1.5x(10** -45) до 3.4x(10**38) с 7-ю - 8-ю
значащими цифрами.

     - Тип Double занимает 8 байт памяти,  обеспечивая диапазон  от
5.0х(10**  -324) до 1.7х(10** 308) с 15-ю - 16-ю значащими цифрами.

     - Тип  Extended - наибольший вещественный тип,  поддерживаемый
8087. Он занимает 10 байт памяти, обеспечивая диапазон от 3.4х(10 *
*  -4932) до 1.1х(10** 4932) с 19-ю - 20-ю значащими цифрами. Любая
арифметическая операция  с  вещественным   типом   производится   с
диапазоном и точностью типа Extended.

     - Тип  Comp  сохраняет  целые  значения  в  8  байтах  памяти,
обеспечивая диапазон от -2 ** 63+1 до 2 ** 63-1, что примерно равно
от -  9.2х(10**18)  до 9.2х(10**18).  Comp можно сравнить с LongInt
двойной точности,  но рассматривается как  вещественный  тип  из-за
того,  что  все  операции  Comp  используют  8087.  Comp удобен для
использования в экономических расчетах.

     В независимости от того,  есть ли 8087 или нет,  6-ти байтовый
тип Real доступен всегда, так что вам не потребуется модифицировать
исходный код для использования 8087.  Вы можете всегда читать файлы
с  данными,  сгенерированными программой,  в которой использовалась
программная библиотека вещественных чисел.
     Заметим однако,  что  8087  обрабатывает  переменные типа Real
значительно медленнее,  чем другие типы. Это происходит из-за того,
что  8087  не  может  прямо обрабатывать формат Real.  Вместо этого
вызывается  программа,  преобразующая  значение  типа  Real  в  тип
Extended  до  операции  над  ним.  Если  Bы  хотите  оптимизировать
скорость выполнения и никогда не будете использовать ЭВМ  без 8087,
вам лучше использовать только типы Single, Double, Extended и Comp.


                      Расширенная арифметика.

     Тип Extended является основой для всех вычислений  с плавающей
точкой  для  8087.  Turbo  Pascal  использует  формат  Extended для
хранения всех вещественных констант и  вычисляет  все  вещественные
выражения,  используя точность Extended.  Так например,  вся правая
часть  следующего  оператора,  будет  вычисляться  в  Extended   до
преобразования в тип левой части:

     {$N+}
     var
        X, A, B, C: Real;
     begin
        X := (B + Sqrt(B * B - A * C)) / A;
     end;

     Без специальных усилий программиста Turbo  Pascal обеспечивает
вычисления,   используя   точность   и   диапазон   типа  Extended.
Дополнительная  точность  означает  меньшую  ошибку  округления,  и
дополнительный  диапазон означает более редкие ошибки переполнения.
     Вы можете улучшить автоматическое использование Extended Turbo
Pascal.  Например,  вы  можете  объявить  переменные типа Extended,
используемые  для  промежуточных  результатов.   Следующий   пример
вычисляет сумму:

     var
        Sum: 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;

     Если бы Т была объявлена как Single,  то каждое присваивание T
в цикле давало бы ошибку  округления  из-за  ограниченной  точности
Single. Но   так  как  Т  -  Extended,  то  все  ошибки  округления
ограничены точностью Extended за исключением присваивания T  в Sum.
Меньшая ошибка округления означает более точный результат.
     Вы можете также  объявить  формальные  параметры  и  результат
функций    типа    Extended.    Это   предотвращает   необходимость
преобразования между числовыми форматами,  что могло бы привести  к
потере точности. Например:

     function Area(Radius: Extended): Extended;
     begin
        Area := Pi * Radius * Radius;
     end;



                   Сравнение вещественных типов.

     Поскольку вещественные  типы   являются   аппроксимированными,
результат  сравнения  двух  различных  вещественных типов не всегда
очевиден. Например, если X - переменная типа Single, а Y переменная
типа Double, то следующие операторы будут давать False :

     X := 1/3;
     Y := 1/3;
     Writeln( X = Y);

     Это происходит из-за того,  что X имеет точность 7 - 8 цифр, а
Y - 15 - 16 ;  и когда обе  преобразуются  в  Extended,  они  имеют
отличие после 7 - 8 цифр. Аналогично, операторы

     X := 1/3;
     Writeln( X = 1/3);

     будут выдавать False поскольку результат 1/3 в  WriteLn  будет
вычисляться с 20-ю значащими цифрами.


                       Стек вычисления 8087.

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

     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).  Каждый  рекурсивный  вызов  будет  сохранять  значения в
стеке,  приводя  к  переполнению   на   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
выводят 4 цифры вместо 2 для экспоненты числа с  плавающей  точкой,
чтобы   обеспечить   диапазон   для   типа  Еxtended.  Кроме  того,
стандартная процедура Str  возвращает  4  цифры  экспоненты,  когда
выбран вещественный формат.


                    Модули, использующие 8087.

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

     Примечание: Когда   Вы   компилируете   в  состоянии  числовой
обработки, {$N+} возвращаемое значение программ с  плавающей точкой
в модуле System - Sqrt,  Pi,  Sin и т.д. будут типа Extended вместо
Real.


                         Обнаружение 8087.

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

     SET 87 = Y

     или

     SET 87 = N

     Устанавливая переменную  среды  87  в  N,  Вы  говорите  Turbo
Pascal,  что не хотите использовать 8087, даже если он есть на ЭВМ.
Соответственно,  присваивание переменной  87  =  Y,  означает,  что
сопроцессор есть и Вы хотите использовать его.

     ВНИМАНИЕ! Если  Вы установили 87 = Y ,  а 8087 нет на ЭВМ,  то
выполнение Вашей программы приведет к аварийному завершению.

     Если переменная 87  была  определена  (любое  значение)  и  Вы
хотите отменить его, введите

     SET 87 =

и после этого нажмите Enter.

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

————————————————————————————————————
  Значение         Описание
————————————————————————————————————
     0          сопроцессора нет
     1          обнаружен 8087
     2          обнаружен 80287
     3          обнаружен 80387
————————————————————————————————————

     Ваша программа  может  проверить  переменную  Test8087,  чтобы
определить  конфигурацию  ЭВМ.  В  частности,  Test8087  может быть
проверена  для  того,  чтобы  определить  -  вещественные  операции
эмулируются или выполняются напрямую.


                    Эмуляция 8087 в Ассемблере.

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