Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

VPS в 21 локации

От 104 рублей в месяц

Безлимитный трафик. Защита от ДДоС.

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Оверлеи (VROOMM)

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

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

Оверлеи используются только в 16-разрядных программах DOS. В приложениях Windows для сокращения объема используемой памяти вы можете пометить сегменты как Discardable (выгружаемые).

Работа программ с оверлеями

Программа управления оверлеями (VROOMM, или Virtual Run-time Object-Oriented Memory Manager) выполняет за вас большую часть работы по организации оверлеев. В обычных оверлейных системах модули группируются в базовый и набор оверлейных модулей. Подпрограммы в данном оверлейном модуле могут вызывать подпрограммы из этого же модуля и из базового модуля, но не из других модулей. Оверлейные модули перекрывают друг друга, т.е. одновременно в памяти может находиться только один оверлейный модуль, и все они при активизации занимают один и тот же участок физической памяти. Общий объем памяти, необходимой для запуска данной программы, определяется размером базового, плюс максимального оверлейного модуля. Эта обычная схема не обеспечивает достаточной гибкости. Она требует полного учета всех возможных обращений между модулями программы и, соответственно, планируемой вами, группировки оверлеев. Если вы не можете разбить вашу программу в соответствии со взаимозависимостью обращений между ее модулями, то вы не сможете и разбить ее на оверлеи. Схема VROOMM совершенно иная. Она обеспечивает динамический свопинг сегментов. Основной единицей свопинга является сегмент. Сегмент может состоять из одного или нескольких модулей. И что еще более важно, любой сегмент может вызывать любой другой сегмент. Вся память делится на базовую область и область свопинга. Как только встречается вызов функции, которая не находится ни в базовой, ни в области свопинга, сегмент, содержащий вызываемую функцию, помещается в область свопинга, возможно, выгружая оттуда при этом другие сегменты. Это мощное средство - подобное виртуальной программной памяти. От вас больше не требуется разбивать код на статические, отдельные оверлейные блоки. Вы просто запускаете программу!

Что происходит, когда возникает необходимость поместить сегмент в область свопинга? Если эта область имеет достаточно свободного места, то данная задача выполняется просто. Если же нет, то из области свопинга, чтобы искомая свободная область освободилась, должен быть выгружен один или более сегментов. Как выбрать сегменты для выгрузки? Действующий здесь алгоритм очень сложен. Упрощенная версия его такова: если в области свопинга имеется неактивный сегмент, то для выгрузки выбирается он. Неактивными считаются сегменты, в которых в текущий момент нет выполняемых функций. В противном случае берется активный сегмент. Удаление сегментов из памяти продолжается до тех пор, пока в области свопинга не образуется достаточно свободной памяти для размещения там требуемого сегмента. Такой метод называется динамическим свопингом.

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

После загрузки оверлея в память он помещается в оверлейный буфер, который расположен в памяти между сегментом стека и дальней динамически распределяемой областью. По умолчанию размер оверлейного буфера вычисляется и устанавливается при загрузке программы, но его можно изменить при помощи глобальной переменной _ovrbuffer. Если достаточный размер памяти недоступен, то появляется либо сообщение об ошибке DOS ("Program too big to fit in memory" - "Программа слишком велика для имеющейся памяти").

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

При использовании оверлеев память распределяется, как показано на следующем рисунке:

Распределение памяти для оверлейных структур


                         Модель MEDIUM                   Модель LARGE



                                                                       



                         класс CODE      Резидентный   класс CODE      



                                         код                           



                                                                       



       Эти сегменты      класс OVRINFO   Данные для    класс OVRINFO   



       генерируются                      управления                    



       компоновщиком                     оверлеями                     



       автоматически                                                  



                         класс STUBSEG   Один сегмент  класс STUBSEG   



                                         stub для                      



                                         каждого                       



                                         оверлейного                   



                                         сегмента                      



                                                                       



                                _DATA                         _DATA    



       Ближняя динами-     класс DATA                    класс DATA    



       чески распреде-                                                 



       ляемая область и    ближняя куча     Отдельный                  



       стек совместно    ^                  сегмент    ^               



       используют сег-          стек        стека             стек     



       мент данных                                                     



                        оверлейный буфер              оверлейный буфер 



                        (распределяется               (распределяется  



                         при загрузке)                 при загрузке)   



                                                                       



                              дальняя                       дальняя    



                           динамически                   динамически   



                          распределяемая                распределяемая 



                         v   область                   v   область     



                                                                       







                                                       Модель HUGE



                                                                      



                                       Резидентный    класс CODE      



                                       код                            



                                                                      



                   Эти сегменты        Данные для     класс OVRINFO   



                   генерируются        управления                     



                   компоновщиком       оверлеями                      



                   автоматически                                     



                                       Один дополни-  класс STUBSEG   



                                       тельный сег-                   



                                       мент для                       



                                       каждого                        



                                       оверлейного                    



                                       сегмента                       



                                                                      



         . . .                         Несколько                      



                                       сегментов                      



                                       данных                         



                                                                      



                                       Отдельный                      



                                       сегмент        ^               



                                       стека                 стек     



                                                                      



                                                     оверлейный буфер 



                                                     (распределяется  



                                                      при загрузке)   



                                                                      



                                                           дальняя    



                                                        динамически   



                                                       распределяемая 



                                                      v    область    



Оптимальное использования оверлеев Borland C++

Для полного использования преимуществ оверлейных структур, создаваемых Borland C++, сделайте следующее:

  • Минимизируйте резидентный код (резидентные библиотеки исполняющей системы, обработчики прерываний и драйверы устройств).
  • Установите размер оверлейного пула таким образом, чтобы добиться наиболее комфортных условий для создаваемой программы (начните со 128К и регулируйте этот размер вверх и вниз, пока не установите желаемое соотношение между быстродействием и размером программы).
  • Подумайте об универсальности и многосторонности создаваемого кода: воспользуйтесь преимуществами оверлейной структуры и обеспечьте поддержку обработки специальных случаев, интерактивную справочную систему по программе и прочие не рассматриваемые здесь средства, являющиеся достоинствами с точки зрения конечного пользователя.

Требования

При создании оверлеев следует помнить несколько простых правил, а именно:

  • Минимальная часть программы, которая может выделяться в качестве оверлея, это сегмент.
  • Прикладные программы с оверлейной структурой должны иметь одну из трех следующих моделей памяти: medium , large или huge; модели tiny, small и compact оверлеи не поддерживают.
  • Перекрывающиеся сегменты подчиняются обычным правилам слияния сегментов. То есть, в одном и том же сегменте может участвовать несколько объектных файлов.

Генерация оверлеев во время компоновки полностью не зависит от управления сегментами во время исполнения программы; компоновщик не включает автоматически каких-либо кодов для управления оверлеями. Действительно, с точки зрения компоновщика программа управления оверлеями является просто одним из подлежащих компоновке участков кода. Единственное предположение, которое делает компоновщик, состоит в том, что программа управления оверлеями воспринимает вектор прерываний (обычно INT 3FH), через который происходит управление динамической загрузкой. Такой уровень "прозрачности" упрощает создание пользовательских программ управления оверлеями, наилучшим образом управляющих требованиям конкретной прикладной программы.

Оверлеи и обработка исключительных ситуаций

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

  • Не расширяемые подставляемые функции.
  • Шаблоны функций.
  • Функции-элементы или шаблоны классов.

Конструкция обработки исключительной ситуации включает в себя написанный пользователем блок try/catch и __try/__except. Кроме того, компилятор также может включать обработчики исключительных ситуаций и блоки с локальными динамическими переменными, спецификациями исключительных ситуаций и некоторые выражения new/delete.

Если вы пытаетесь использовать в оверлее вышеуказанные конструкции обработки исключительных ситуаций, компоновщик идентифицирует функцию и модуль следующим сообщением:


            Error: Illegal local public in функция in module модуль



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

  • удалить из функции все конструкции обработки исключитель ной ситуации;
  • удалить функцию их оверлейного модуля.

Особенно внимательно нужно строить оверлейную программу, которая использует множественное наследование. Попытка создать оверлейный модуль, который определяет или использует конструкторы или деструкторы классов с множественным наследованием может привести к тому, что компоновщик будет генерировать следующее сообщение об ошибке:


            Error: Illegal local public in класс: in module модуль





Когда генерируется такое сообщение, идентифицированный компоновщиком модуль не следует делать оверлейным.

В классе контейнера (в BIDS?.LIB) есть механизм обработки исключительной ситуации, который по умолчанию выключен. Однако диагностическая версия генерирует исключительные ситуации и не может использоваться в оверлеях. По умолчанию класс string может генерировать исключительные ситуации, и его не следует использовать в программах с оверлеями.

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

Для создания программы с оверлейной структурой все ее модули должны компилироваться с включенным параметром компилятора -Y. Для того, чтобы сделать оверлейным конкретный модуль, его следует компилировать с параметром -Yo. (-Yo автоматически включает параметр -Y).

Параметр -Yo распространяется на все модули и библиотеки, следующие за ней в командной строке компилятора BCC. Отменить ее можно, задав -Yo-. Эти два параметра являются единственными параметрами командной строки, которые могут следовать после имен файлов. Например, для того, чтобы сделать оверлейным модуль OVL.C, но не библиотеку GRAPHICS.LIB, можно использовать любую из следующих командных строк:


            BCC -ml - Yo ovl.c -Yo- graphics.lib





или

            BCC -ml graphics.lib -Yo ovl.c



Если при запуске компоновщика TLINK явно задана компоновка файла .EXE, то в командной строке компоновщика должен задаваться параметр /o.

Предположим, вы хотите иметь оверлейную структуру в программе, состоящей из трех модулей: MAIN.C, O1.C и O2.C. Оверлеями должны являться модули O1.C и O2.C. (Программа MAIN.C содержит зависящие от текущего времени подпрограммы и обработчики прерываний и потому должна оставаться резидентной). Предположим, что данная программа использует модель памяти large.

Следующая команда позволяет выполнить данную задачу:


            BCC -ml -Y main.c -Yo o1.c o2.c



В результате получится выполняемый файл MAIN.EXE с двумя оверлеями.

Разработка программ с оверлеями

Этот раздел содержит важные сведения о разработке программ с оверлеями с хорошими характеристиками.

При компиляции оверлейного модуля вы должны использовать большую модель памяти (medium, large или huge). При всяком вызове функции из оверлейного модуля вы обязаны гарантировать, что все активные в текущий момент функции являются дальними.

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

Невыполнение требования дальних вызовов в оверлейной программе приведет при выполнении программы к непредсказуемым и возможно, катастрофическим результатам.

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

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


            unsigned _ovrbuffer = 0x2000;



Общей формулы для определения идеального размера оверлейного буфера не существует.

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

Программа управления оверлеями Borland C++ полностью поддерживает передачу оверлейных функций как аргументов, присвоение и инициализацию переменных типа указателя функции, адресующих оверлейные функции, а также вызов оверлейных подпрограмм через указатели функций.

Отладка оверлейных программ

Большинство отладчиков либо имеет весьма ограниченные средства отладки программ с оверлейной структурой, либо вообще не имеет таких средств. Иначе дело обстоит с интегрированным со средой разработки программ отладчиком Borland C++ и автономным отладчиком фирмы Borland (Turbo Debugger). Оба эти отладчика полностью поддерживают пошаговую отладку и установку точек останова в оверлеях совершенно "прозрачным" для вас способом. Благодаря использованию оверлеев вы имеете возможность легко разрабатывать и отлаживать громоздкие прикладные программы - как в интегрированной среде, так и при помощи Turbo Debugger.

Внешние подпрограммы в оверлеях

Подобно обычным функциям языка Си, внешние (external) подпрограммы на языке Ассемблера, чтобы хорошо работать с подсистемой управления оверлеями, должны подчиняться некоторым правилам.

Если подпрограмма на языке ассемблера выполняет вызов любой оверлейной функции, то такая подпрограмма должна иметь объявление FAR и устанавливать границу стека при помощи регистра BP. Например, если OtherFunc - это оверлейная функция в другом модуле, и ее вызывает подпрограмма на языке Ассемблера ExternFunc, то тогда ExternFunc должна быть дальней (FAR) и устанавливать границы стека, как показано ниже:


          ExternFunc     PROC   FAR



                  push   bp            ; сохранить bp



                  mov    bp,sp         ; установить стек



                  sub    sp,LocalSize  ; распределить локальные



                                       ; переменные



                  ...



                  call   OtherFunc     ; вызов другого оверлейного



                                       ; модуля



                  ...



                  mov    sp,bp         ; освобождение локальных



                                       ; переменных



                  pop    bp            ; восстановление BP



                  RET                  ; возврат



          ExternFunc     ENDP



где LocalSize - это размер локальных переменных. Если LocalSize равен нулю, вы можете опустить две строки распределения и освобождения локальных переменных, но ни в коем случае нельзя опускать установку границ стека BP, даже если аргументов и переменных в стеке нет.

Эти требования остаются теми же в случае, когда ExternFunc делает косвенные ссылки на оверлейные функции. Например, если OtherFunc вызывает оверлейные функции, но сама не является оверлейной, то ExternFunc должна быть FAR и также должна устанавливать границы стека.

В случае, когда ассемблерная подпрограмма не делает ни прямых, ни косвенных ссылок на оверлейные функции, то специальные требования отсутствуют; подпрограмма на языке Ассемблера может быть объявлена как NEAR. Она не обязана устанавливать границ стека.

Оверлейные подпрограммы на языке ассемблера не должны создавать переменные в кодовом сегменте, поскольку все изменения, внесенные в оверлейный кодовый сегмент, теряются при освобождении оверлея. Подобным же образом, указатели объектов, расположенных в оверлейных сегментах, не сохраняют достоверность после вызова других оверлеев, поскольку программа управления оверлеями свободно перемещает и освобождает оверлейные кодовые сегменты в памяти.

Свопинг

Если в системе компьютера установлена дополнительная или расширенная память, вы можете сообщить программе управления оверлеев, что он должен использовать эту память при свопинге. В этом случае при удалении модуля из оверлейного буфера (когда туда требуется загрузить новый модуль, а буфер полон) программа управления оверлеями может поместить удаляемый модуль в эту память. При любой последующей загрузке этого модуля за счет того, что модуль перемещается в памяти, а не считывается с диска, экономится время.

В обоих случаях есть две возможности: программа управления оверлеями может либо обнаруживать наличие дополнительной или расширенной памяти самостоятельно и затем брать на себя управление этой памятью, либо использовать уже обнаруженную и распределенную часть такой памяти. В случае расширенной памяти обнаружение памяти не во всех случаях выполняется удачно, поскольку многие программы кэширования памяти и программы организации виртуального диска могут использовать эту память, не делая об этом никаких отметок. Чтобы избежать этих проблем, вы должны сообщить программе управления оверлеями начальный адрес расширенной памяти и какой участок ее можно безопасно использовать. Borland С++ предусматривает две функции, которые позволяют вам инициализировать расширенную и дополнительную память - _OvrInitEms и _OvrInitExt.

Математические операции

Ниже рассматриваются возможности работы с числами с плавающей точкой и объясняется, как использовать математические операции с комплексными числами.

Операции ввода-вывода с плавающей точкой

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


            /* Подготовка к выводу чисел с плавающей точкой */



            #include <stdio.h>





            #pragma extref _floatconvert





            void main() {



               printf(*d = %f\n", 1.3);



               }



Сопроцессор

Си работает с двумя числовыми типами: целыми (int, short, long и т.д.) и с плавающей точкой (float double и long double). Процессор вашего компьютера легко справляется с обработкой чисел целых типов, однако числа с плавающей точкой отнимают больше времени и усилий.

Семейство процессоров iAPx86 имеет сопутствующее ему семейство математических сопроцессоров - 8087, 80287 и 80387. Мы будем обозначать все семейство математических сопроцессоров 80x87 термином "сопроцессор". (В случае процессора 80487 вы имеете математический сопроцессор уже встроенным в основной.)

Процессор 80х87 представляет собой специальный аппаратно реализованный числовой процессор, который можно установить на вашем PC. Он служит для выполнения с большой скоростью команд с плавающей точкой. При большом количестве в вашей программе операций с плавающей точкой вам, безусловно, нужен сопроцессор. Блок центрального процессора в вашем компьютере осуществляет интерфейс с 80х87 по специальным шинам интерфейса.

Эмуляция платы 80х87

По умолчанию в Borland C++ устанавливается параметр генерации кода "эмуляция" (параметр компилятора режима командной строки -f). Этот параметр предназначен для программ, которые могут вообще не иметь операций с плавающей точкой, а также для программ, которые должны выполняться и на машинах, на которых сопроцессор 80х87 не установлен.

В случае параметра эмуляции компилятор генерирует код, как если бы сопроцессор присутствовал, но при компоновке подключает библиотеку эмуляции операций с плавающей точкой (EMU.LIB). При выполнении такой программы сопроцессор 80х87, если он установлен, будет использоваться. Если же во время выполнения процессора не окажется, то программа будет использовать специальное программное обеспечение, эмулирующее процессор 80х87.

Использование кода 80х87

Если вы планируете использовать вашу программу исключительно на машинах с установленным математическим сопроцессором 80х87, то можно сэкономить около 10К памяти программы, опустив из нее логику автоматического определения присутствия процессора 80х87 и эмулятора. Для этого следует просто выбрать параметр генерации кода операций с плавающей точкой при наличии сопроцессора 80х87 (или параметр компилятора режима командной строки -f87). Borland C++ в этом случае скомпонует вашу программу с библиотекой FP87.LIB (вместо EMU.LIB).

Получение кода без операций с плавающей точкой

При отсутствии в программе операций с плавающей точкой вы можете сэкономить немного времени компиляции, выбрав значение параметра генерации операций с плавающей точкой None ("отсутствуют") (или параметр компилятора командной строки -f-). Тогда Borland C++ не будет выполнять компоновку ни с библиотекой EMU.LIB, ни с FP87.LIB, ни с MATHx.LIB.

Параметр быстрых вычислений с плавающей точкой

Borland C++ имеет параметр быстрых вычислений с плавающей точкой (параметр компилятора режима командной строки -ff). Выключить этот параметр можно при помощи параметра командной строки -ff-. Его назначение состоит в выполнении некоторой оптимизации, противоречащей правильной семантике языка Си. Например:


            double x;



            x = (float)(3.5*x);



Для вычисления по обычным правилам x умножается на 3.5, давая точность результата double, которая затем усекается до точности float, после чего x записывается как double. При использовании параметра быстрых вычислений с плавающей точкой произведение типа long double преобразуется непосредственно в double. Поскольку лишь очень немногие программы чувствительны к потере точности при преобразовании от более точного к менее точному типу с плавающей точкой, то данный параметр используется по умолчанию.

Переменная операционной среды 87

При построении программы с эмуляцией сопроцессора 80x87, которая устанавливается по умолчанию, ваша программа станет автоматически проверять наличие сопроцессора 80х87 и использовать его, если он установлен в машине.

Существует ряд ситуаций, в которых вам может понадобиться отменить режим автоматического определения наличия сопроцессора по умолчанию. Например, ваша собственная исполняющая система может иметь сопроцессор 80х87, но вам требуется проверить, будет ли программа работать так, как вы предполагали, в системе без сопроцессора. Либо ваша программа предназначена для работы в системе, совместимой с PC, но данная конкретная система возвращает логике автоматического определения наличия сопроцессора неверную информацию (либо при отсутствии сопроцессора 80х87 говорит, что он на месте, либо наоборот).

Borland C++ имеет параметр для переопределения логики определения наличия сопроцессора при загрузке программы. Этот параметр - соответствующая переменная операционной среды системы 87. Переменная операционной среды 87 устанавливается в ответ на подсказку DOS при помощи команды SET:


            C>SET 87=N



       или



            C>SET 87=Y



Ни с какой стороны знака равенства не должно быть пробелов. Установка переменной операционной среды 87 в N говорит загрузочному коду исполняющей системы о том, что вы не хотите использовать сопроцессор 80х87 даже в том случае, если он установлен в системе.

Установка переменной операционной среды в значение Y означает, что сопроцессор на месте и вы желаете, чтобы программа его использовала. Программист должен знать следующее: если установить 87=Y, а физически сопроцессор 80х87 в системе не установлен, то система "зависнет".

Если переменная операционной среды 87 была определена (с любым значением), и вы желаете сделать ее неопределенной, введите в ответ на подсказку DOS:


            C>SET=



Непосредственно после знака равенства нажмите клавишу Enter, и переменная 87 станет неопределенной.

Регистры и сопроцессор 80х87

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

  1. В режиме эмуляции сопроцессора 80х87 циклический переход в регистрах, а также ряд других особенностей 80х87 не поддерживается.
  2. Если вы смешиваете операции с плавающей точкой и встроенные коды на языке Ассемблера, то при использовании регистров следует должны принимать некоторые меры предосторожности. Это связано с тем, что набор регистров сопроцессора 80х87 перед вызовом функции в Borland C++ очищается. Вам может понадобиться извлечь из стека и сохранить регистры сопроцессора 80х87 до вызова функции, использующей сопроцессор, если вы не уверены, что свободных регистров достаточно.

Отмена обработки особых ситуаций для операций с плавающей точкой

По умолчанию программа Borland C++ в случае переполнения или деления на ноль в операциях с плавающей точкой аварийно прерывается. Вы можете замаскировать эти особые ситуации для операций с плавающей точкой, вызывая в main _control87 перед любой операцией с плавающей точкой. Например:


            #include <floar.h>



            main() {



               _control87(MCW_EM,MCW_EM);



               ...



            }



Можно определить особую ситуацию для операции с плавающей точкой, вызвав функции _status87 или _clear87.

Определенные математические ошибки могут также произойти в библиотечных функциях, например, при попытке извлечения квадратного корня из отрицательного числа. По умолчанию в таких случаях выполняется вывод на экран сообщений об ошибке и возврат значения NAN (код IEEE "not-a-number" - "не число"). Использование NAN (нечисловых значений) скорее всего приведет далее к возникновению особой ситуации с плавающей точкой, которая в свою очередь вызовет, если она не замаскирована, аварийное прерывание программы. Если вы не желаете, чтобы сообщение выводилось на экран, вставьте в программу соответствующую версию matherr.


           #include <math.h>



           int cdecl matherr(struct exception *e)



           {



              return 1;     /* ошибка обработана */



           }





Любое другое использование matherr для внутренней обработки математических ошибок недопустимо, так как она считается устаревшей и может не поддерживаться последующими версиями Borland C++.

Математические операции с комплексными числами

Комплексными называются числа вида x + yi, где x и y - это вещественные числа, а i - это корень квадратный из -1. В Borland C++ всегда существовал тип:


            struct complex



            {



               double x, y;



            };



определенный в math.h. Этот тип удобен для представления комплексных чисел, поскольку их можно рассматривать в качестве пары вещественных чисел. Однако, ограничения Си делают арифметические операции с комплексными числами несколько громоздкими. В С++ операции с комплексными числами выполняются несколько проще.

Для работы с комплексными числами в С++ достаточно включить файл complex.h. В complex.h для обработки комплексных чисел переопределены:

  • все обычные арифметические операции;
  • операции потоков >> и <<;
  • обычные арифметические функции, такие как sqrt и log.

Библиотека complex активизируется только при наличии аргументов типа complex. Таким образом, для получении комплексного квадратного корня из -1 используйте:


            sqrt(complex(-1))



       а не



            sqrt(-1)



Использование двоично-десятичной арифметики (BCD)

Borland C++, также как и большинство прочих компьютеров и компиляторов, выполняет математические вычисления с числами в двоичном представлении (то есть в системе счисления с основанием 2). Это иногда путает людей, привыкших исключительно к десятичной математике (в системе счисления с основанием 10). Многие числа с точным представлением в десятичной системе счисления, такие как 0.01, в двоичной системе счисления могут иметь лишь приближенные представления.

В большинстве прикладных программ двоичные числа предпочтительны, однако в некоторых ситуациях ошибка округления в преобразованиях между системами счисления с основаниями 2 и 10 нежелательна. Наиболее характерным случаем здесь являются финансовые или учетные задачи, где предполагается сложение центов. Рассмотрим программу, складывающую до 100 центов и вычитающую доллар:


            #include <stdio.h>



            int i;



            float x =0.0;



            for (i = 0; i < 100; ++i)



               x += 0.01;



            x -= 1.0;



            print("100*.01 - 1 = %g\1",x);



Правильным ответом является 0.0, однако ответ, полученный данной программой, будет малой величиной, близкой к 0.0. При вычислении ошибка округления, возникающая во время преобразования 0.01 в двоичное число, накапливается. Изменение типа x на double или long double только уменьшает ошибку вычисления, но не устраняет ее вообще.

Для решения этой проблемы Borland C++ предлагает специфический для C++ тип bcd (двоично-десятичный), объявленный в файле bcd.h. В случае двоично-десятичного представления число 0.01 будет иметь точное значение, а переменная x типа bcd даст точное исчисление центов.


            #include <bcd.h>



            int i;



            bcd x = 0.0;



            for (i = 0; i < 100; ++i)



               x += 0.01;



            x -= 1.0;



            cout << "100*0.1 - 1 = " << x << "\n";



При этом необходимо учитывать следующие особенности типа bcd:

  • bcd не уничтожает ошибку округления вообще. Вычисление типа 1.0/3.0 все равно будет иметь ошибку округления.
  • Обычные математические функции, такие как sqrt и log, для аргументов с типом bcd переопределяются.
  • Числа типа bcd имеют точность представления около 17 разрядов и диапазон принимаемых значений от 1x10^-125 до 1x10^125.

Преобразования двоично-десятичных чисел

Тип bcd - это определяемый тип, отличный от float, double или long double. Десятичная арифметика выполняется только когда хотя бы один операнд имеет тип bcd.

Для преобразования двоично-десятичного числа обратно к обычной системе счисления с основанием 2 (тип float, double или long double), служит функция-элемент real класса bcd. Функция real выполняет все необходимые преобразования к типам long, double или lognd double, хотя такое преобразование не выполняется автоматически. Функция real выполняет все необходимые преобразования к типу long double, который может быть затем преобразован к другим типам при помощи обычных средств языка Си. Например:


            /* Печать чисел BCD */



            #include <bcd.h>



            #include <iostream.h>



            #include <stdio.h>





            void main(void) {



               bcd a = 12.1;



               double x = real(a);   /* преобразование для печати



               printf("\na = %Lg", real(a));



               printf("\na = %g", (double)real(a));



               cout << "\na =" << a; /* рекомендуемый метод



Отметим, что поскольку printf не выполняет контроль типа аргументов, спецификатор формата должен иметь L, если передается значение real(a) типа long double.

Число десятичных знаков

Вы можете задать, сколько десятичных знаков должно участвовать в преобразовании из двоичного типа в bcd. Это число является вторым, необязательным аргументом в конструкторе bcd. Например, для преобразования $1000.00/7 в переменную bcd, округленную до ближайшего цента, можно записать:


            bcd a = bcd(1000.00/7, 2)





где 2 обозначает два разряда после десятичной точки. Таким образом:

            1000.00/7           =   142.85714



            bcd(1000.00/7, 2)   =   142.860



            bcd(1000.00/7, 1)   =   142.900



            bcd(1000.00/7, 0)   =   142.000



            bcd(1000.00/7, -1)  =   140.000



            bcd(1000.00/7, -2)  =   100.000





Округление происходит по банковским правилам, что означает округление до ближайшего целого числа, причем в случае одинакового "расстояния" до ближайшего целого в прямую и обратную сторону округление выполняется в сторону четного. Например:


            bcd(12.335, 2)      =   12.34



            bcd(12.245, 2)      =   12.34



            bcd(12.355, 2)      =   12.36





Такой метод округления задается стандартом IEEE.

Назад | Содержание | Вперед
Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...