В 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, предусматривается четыре новых вещественного типа:

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

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

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

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

Исполняющая библиотека 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). Для нее определены следующие значения:

  ЪДДДДДДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї

   і  Значение і   Определение   і

   ГДДДДДДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ

   і 0   і   сопроцессор не обнаружен  і

   і 1   і   обнаружен сопроцессор 8087   і

   і 2   і   обнаружен сопроцессор 80287  і

   і    3   і  обнаружен сопроцессор 80387  і

  АДДДДДДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

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

Операционная среда 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 отсутствует');

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