Глава 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.
                       Назад | Содержание | Вперед