Предисловие Предлагаемый вниманию читателей том трудов Института системного программирования РАН посвящен проекту Центра верификации операционной системы Linux, созданного на базе нашего института, по разработке тестового набора для проверки соответствия стандарту Linux Standard Base. Тестовый набор строится на базе формальных спецификаций требований стандарта и призван обеспечить как аккуратное прослеживание обнаруживаемых нарушений к нарушаемым ими требованиям, так и удобное сопровождение тестов, их конфигурирование, внесение модификаций в результате изменения каких-то требований и перенацеливание тестов на работу на другой платформе. Этот проект финансируется Федеральным агентством по науке и инновациям Министерства образования и науки Российской Федерации и имеет две основные цели. Одна цель, вполне технологическая, — это повышение совместимости и надежности работы систем на базе различных версий Linux. Вторая цель носит более просветительский характер — это популяризация передовых технологий разработки программного обеспечения (ПО) и контроля его качества в сообществе разработчиков программ с открытым исходным кодом, повышение общего уровня качества открытых разработок за счет внедрения новых методов построения сложных программных систем. В проекте участвуют не только опытные исследователи ИСП РАН, многие из которых сами являются создателями передовых методов разработки ПО, но и студенты и аспиранты ведущих российских ВУЗов, выпускающих специалистов в области программной инженерии. Таким образом, просветительские и образовательные аспекты проекта находят свое воплощение и в подготовке будущих экспертов, обладающих как широкими знаниями, так и практическими навыками использования этих передовых методик при работе в реальных, достаточно больших проектах. Несмотря на большой объем работ — один только текст стандарта Linux Standard Base и других стандартов, на которые он ссылается: POSIX, XOpen/Curses, System V Interface Definition, международный стандарт языка C, и пр., занимает около 6000 страниц — проект успешно движется к завершению в конце 2006 года. С его текущими результатами можно ознакомиться на сайте Центра верификации Linux, http://www.linuxtesting.ru. Данный сборник содержит статьи участников этого проекта, содержащие, в основном, результаты исследований, проводимых в его рамках или по поводу тех или иных задач, возникающих в ходе выполнения его работ. Первые три статьи, однако, носят скорее информативный, а не исследовательский характер, и повествуют о проблемах совместимости в мире Linux-систем и попытках Linux-сообщества прийти к единым стандартам на интерфейс взаимодействия между операционной системой и приложениями, что и делает проводимый в ИСП РАН проект актуальным.
применению Linux в государственных организациях и связанным с этим выгодам и проблемам. Для многих областей, в которых используется Linux в этом контексте, важны как ее надежность, так и возможность надежного развития систем, построенных на базе этой операционной системы. И то, и другое требует выхода технологий разработки, используемых при создании открытого кода, на более высокий уровень — они должны обогатиться эффективными техниками отслеживания качества создаваемого кода, для чего отлаженные и легко конфигурируемые тестовые наборы являются необходимой основой. Работа А. Гриневича, Д. Марковцева и В. Рубанова «Проблемы совместимости Linuxсистем» рассказывает о разнообразии современных дистрибутивов этой операционной системы, порождающей те же проблемы несовместимости и непереносимости приложений, которые два десятилетия назад привели производителей Unix-систем к осознанию необходимости их стандартизации и к появлению стандарта POSIX. По тем же самым причинам в настоящее время разработчики Linux-приложений пытаются создать такой же единый стандарт уже не только на программные интерфейсы, а и на бинарные, к тому же охватывающий гораздо более широкое множество функций — Linux Standard Base. Следующая статья «Linux Standard Base: история успеха?» А. Хорошилова посвящена предпосылкам и истории появления стандарта Linux Standard Base, его текущему состоянию и расширениям, которые предполагается внести в следующие версии этого стандарта. Разработчикам приложений под Linux и системным программистам скорее всего будет интересно ознакомиться с планами развития одного из наиболее активно развивающихся стандартов в области открытого ПО, пользующегося широкой поддержкой индустрии. В статье А. Гриневича, В. Кулямина, Д. Марковцева, А. Петренко, В. Рубанова и А. Хорошилова «Использование формальных методов для обеспечения соблюдения программных стандартов» рассказывается об организации процесса разработки тестов на основе формальных спецификаций, используемом в ходе описанного выше проекта. Важную роль в успехе проекта такого масштаба играет адекватная поддержка решения инженерных и организационных задач, возникающих в его ходе. Авторы показывают, что подходящая техника разработки тестов должна не только давать в результате тесты хорошего качества, но и обеспечивать правильное разбиение результатов на модули, поддерживать наглядные связи получаемых тестов с исходными требованиями стандарта, способствовать решению задач управления конфигурациями полученного тестового набора. Статья демонстрирует подходы к решению всех этих проблем, используемые при создании тестового набора для стандарта Linux Standard Base. Работа В. Кулямина «Формальные подходы к тестированию математических функций» представляет методики формирования строгих требований и выбора тестовых данных для аккуратного тестирования реализаций математических функций, работающих с числами с плавающей точкой. Рассматриваются
Первая статья В. П. Иванникова и А. К. Петренко «Задачи верификации ОС Linux в контексте ее использования в государственном секторе» посвящена 5
6
проблемы, с которыми сталкиваются разработчики библиотек математических функций и определяются методы систематической проверки корректности таких библиотек. Представленные в статье техники используются для создания тестов на соответствие Linux Standard Base, поскольку в этом стандарте (а точнее, в стандартах на библиотеки языка C и POSIX, на которые он ссылается) определяется около 250 функций, реализующих известные математические функции для чисел различной точности. Другая статья сборника, посвященная математическим функциям, — работа «Разработка модельной реализации функций Бесселя из стандарта LSB» студента III-го курса МФТИ А. Пономаренко — продолжает тему предыдущей. В этой работе рассматриваются методы вычисления функций Бесселя и строится их комбинация, достаточно эффективная и точная для всех значений параметров. Корректное вычисление таких функций необходимо, чтобы проверять правильность работы соответствующих библиотечных функций операционной системы Linux. Следующая статья «Тестирование в условиях неполной информации. Подход к разработке спецификаций и генерации тестов» А. Камкина предлагает технику тестирования систем, многие важные параметры работы которых или внутренние состояния неизвестны. Эта техника использует трехзначную логику для представления неопределенных вещей и основана на постепенном выяснении недостающей информации в ходе самого тестирования за счет наблюдения за поведением системы. Поначалу такой тест почти не может обнаружить никаких ошибок, поскольку полагает практически любые результаты работы системы корректными. Но на основании получаемых в ходе работы результатов он определяет какие-то параметры, необходимые для выполнения более строгих проверок, и через некоторое время уже не отличается по возможности нахождения ошибок от более традиционных тестов, которые заранее знают, что является корректным результатом, а что — нет. Последняя в сборнике статья А. Арутюняна «Сравнение эффективности обходчиков UniTESK» рассказывает о результатах практического использования различных алгоритмов обхода графов для автоматической генерации тестов. На основе экспериментальных данных в ней проводится сравнение эффективности двух алгоритмов, реализованных в рамках инструмента разработки тестов CTesK, и даются рекомендации по использованию этих алгоритмов при тестировании достаточно сложных систем или тестировании в условиях ограниченных ресурсов. Таким образом, содержание представляемых работ отражает разнообразие задач, с которыми приходится сталкиваться в рамках крупномасштабных проектов по созданию промышленного программного обеспечения и подчеркивает важность подобных проектов для развития науки в целом и прикладных, практико-ориентированных исследований в частности.
7
(например, проект OpenOffice.org, веб-браузер Mozilla и др.), которые готовы обеспечить основные потребности пользователей офисных приложений.
Задачи верификации ОС Linux в контексте ее использования в государственном секторе В. П. Иванников, А. К. Петренко Институт системного программирования РАН (ИСП РАН), Б. Коммунистическая, 25, Москва, Россия E-mail: {ivan,petrenko}@ispras.ru В последние годы наблюдается устойчивый рост интереса к Open Source модели разработки и распространения программного обеспечения (ПО). Интерес растет как к модели в целом, так и к операционной системе (ОС) Linux, в частности. Основным фактором, поддерживающим эту тенденцию, является низкая цена, а часто и бесплатный доступ к открытому ПО. Однако, одного фактора низких цен недостаточно. За последние 5-6 лет существенно повысилось качество OC Linux, построена целая отрасль промышленности, которая занимается распространением, поддержкой, системной интеграцией решений, основанных на этой операционной системе. Эти изменения в технологии и структуре рынка информационных технологий (ИТ) изменили позицию государственных структур и крупнейших корпораций, определяющих основные процессы и приоритеты в области ИТ. Государственные структуры, помимо экономии средств, которую сулят решения на базе Linux, видят в развитии ИТ на основе Linux следующие преимущества.
Выбирая Linux платформу государство уходит от зависимости от монопольного поставщика ПО (часто таким монополистом является Microsoft).
Открытый код снимает многие проблемы, связанные с безопасностью ПО, в частности, проблемы так называемого вредоносного ПО (malware), существующего, например, в виде закладок и вирусов, и проблемы уязвимости ПО по отношению к различным видам атак.
Linux предоставляет легальный, фактически неограниченный доступ к развитию базового ПО, в частности, к добавлению к базовой функциональности специальных возможностей.
Такие крупные корпорации как IBM, HP, Sun, Novell, Intel и другие развернули целые программы по поддержке Linux индустрии. В первую очередь их интерес также обусловлен снижением цены комплексной программно-аппаратной поставки в случае, если выбирается ОС Linux, а не Windows. Это дает конкурентные преимущества на рынке поставщиков комплексных решений. Также важны факторы, связанные с уходом от монополизма Microsoft. Для компаний поставщиков аппаратных платформ (IBM, Intel) важна возможность модификации ОС с целью демонстрации уникальных преимуществ аппаратных решений. Примером международной программы, преследующей такие цели, является Gelato. Цель Gelato — развить Linux и другое базовое ПО с тем, чтобы показать конкурентные преимущества 64-х битной аппаратной платформы Itanium. Еще одним источником интереса для компаний поставщиков программно-аппаратных платформ и крупных комплексных решений является упрощение, а следовательно и удешевление собственно интеграции. С ростом надежности и разнообразия возможностей, которые предоставляются операционной системой (ОС) Linux, она становится все более привлекательной для использования по различному назначению, в том числе для решения задач государственного управления и в системах управления критичных по безопасности. Сторонники модели «открытого кода», на которой построена ОС Linux, рассматривая вопросы надежности, подчеркивают преимущество этой модели, обусловленное многократным и многосторонним анализом исходных текстов ОС огромным сообществом разработчиков Linux. Их утверждения верны, но лишь отчасти. В действительности, многие дефекты ОС удается выявить и даже предотвратить в процессе внесения новых изменений в исходные тексты. Но вся правда состоит в том, что многие дефекты ждут своего выявление в течение долгого времени и далеко не все удается выявить. Это приводит к выводу, что «открытый код», сам по себе, не является панацеей, а для доведения системы до необходимого уровня надежности необходимо использовать весь спектр современных средств контроля качества, в первую очередь современные средства тестирования. Надежность операционной системы имеет несколько измерений и, соответственно, зависит от нескольких факторов, которые, в конечном счете, определяют качество решения задач в системах, базирующихся на данной ОС. Помимо основного фактора в работе ОС — безотказности, имеется и ряд других, в совокупности не менее важных. Речь идет, например, о
До недавнего времени широкому распространению Linux в государственных структурах мешало отсутствие офисных приложений, поддерживающих работу с документами (речь идет о программах типа Microsoft World, Excel, PowerPoint и др.). Сейчас ситуация меняется, в ОС Linux развивается новая линия — Desktop Linux, а также разрабатываются базовые офисные приложения
9
10
безопасности вычислений (защита от несанкционированного доступа, от несанкционированного изменения поведения ОС и др.);
переносимости приложений;
соответствии требованиям стандартам) и др.
различных
стандартов
(например,
сетевым
Поскольку задача повышения надежности ОС Linux во всех измерениях требует огромных усилий и продолжительного времени, необходимо определить наиболее приоритетные направления в решении этой задачи. Таким направлением является доведение надежности Linux до уровня требований стандартов. Успешное решение этой проблемы существенно продвинет нас и в упрощении переносимости приложений, и в обеспечении безопасности вычислений. На Unix- и Linux-системах проблема переносимости приложений, в частности, их установки стоит существенно острее, чем на Windows. Это, с одной стороны, ведет к усложнению процесса установки, и, с другой стороны, негативно сказывается на надежности систем в целом. Решение этой проблемы лежит в русле разработки и внедрения единого стандарта интерфейса взаимодействия между операционной системой и приложениями, который такую совместимость обеспечит. На роль такого стандарта может претендовать стандарт Linux Standard Base (LSB). Он наследует многие требования стандарта открытых систем POSIX, но за счет более строгих требований к интерфейсам позволяет решать проблемы переносимости эффективнее, чем стандарт POSIX. Стандартизация не может ограничиваться только разработкой стандартов. Для того чтобы удостовериться, что некоторая реализация удовлетворяет требованиям стандартов, нужно проводить разнообразные проверки, включая испытания на различных данных, в различных окружениях. Кроме того, существуют и другие виды проверок: проверка наличия всех необходимых составляющих ядра ОС и библиотек, проверка контрольных сумм в дистрибутивных пакетах, проверка соответствия компонентов ядра и библиотек и др. Самым сложным (соответственно, самым качественным) видом проверок является проверка на тестовых примерах, иногда называемая динамическим тестированием. Среди всех видов тестирования выделяется так называемое аттестационное тестирование, которое в первую очередь направлено на подготовку решения о выдаче (или не выдаче) официального свидетельства/сертификата о соответствии продукта требованиям стандарта. В случае LSB в сертификации нуждаются как поставщики операционных систем, реализующих LSB, так и поставщики приложений, которые объявляют, что их продукт будет работать на любой операционной системе, отвечающей требованиям LSB. Сертификация на соответствие LSB, естественно, повышает конкурентные преимущества сертифицированных продуктов. Как правило, аттестационные испытания хорошо формализованы, они определяются набором детальных регламентирующих документов. Сертификацию на соответствие LSB проводит Open Group по поручению FSG (http://www.opengroup.org/lsb/cert/). В отличие от сертификации на соответствие POSIX, LSB сертификация не сопряжена с контактами со специальными аккредитованными сертифицирующими организациями и представляется более простой и прозрачной, как и всё в мире open source. Для того, чтобы показать, что тот или иной продукт удовлетворяет требованиям LSB, необходимо под 11
контролем представителя FSG пропустить официальный предоставить отчеты по результатам пропуска тестов.
набор
тестов
и
Отметим, что сертификационные тесты (как и любые тесты) не могут гарантировать, что реализация не нарушает требований стандарта — отчет по результатам тестирования в лучшем случае лишь констатирует, что «нарушений не обнаружено». К сожалению, такова общая практика в технологиях тестирования. В связи с этим помимо тестирования на соответствие (conformance testing) для более глубокой проверки выполняются функциональные, нагрузочные, стресс тесты, тесты производительности и другие виды тестов. Они необходимы для того, чтобы получить более основательное заключение о тех или иных характеристиках программного продукта. Добротное функциональное тестирование существенно сложнее тестирования на соответствие. Именно по этой причине в дополнение к открытым сертификационным тестам необходимо создавать наборы эффективных тестов, в частности, дающих существенно более высокий уровень доверия к совместимости приложений, работающих под управлением ОС Linux. Итак, ОС Linux уже сейчас готова к широкому использованию в государственном секторе. Однако это не означает, что все технические проблемы, в частности, проблемы переносимости приложений и проблемы надежности ОС уже полностью решены – здесь имеется широкое поле деятельности. В первую очередь речь идет о совершенствовании стандартов и решении вопросов инфраструктуры, обеспечивающей внедрение стандартов и упрощающей разработку ОС и приложений, которые этим стандартам удовлетворяют. Итог состоит в следующем:
для повышения надежности систем, базирующихся на ОС Linux, и, в частности, переносимости приложений нужно вести работы по внедрению современных стандартов;
основным стандартом, который нацелен на решение задач переносимости, является стандарт LSB;
важнейшим механизмом внедрения стандарта LSB является разработка разноплановых и эффективных тестов и разворачивание работ по сертификации операционных систем и приложений на соответствие требованиям стандарта LSB.
Вместе с тем надо отметить, что решение перечисленных задач потребует достаточно много времени и усилий. Для того, чтобы утверждать это можно изучить историю и современное состояние группы стандартов POSIX (Operating System Interface for Computing Environment). POSIX развивается уже около 20 лет, однако до сих пор нельзя сказать, что как текст стандарта, так качество его сертификационного набора теста являются безупречными. В таком положении находятся многие другие стандарты, задающие требования к программным интерфейсам. Разработка качественного набора тестов, проверяющего, насколько реализация интерфейса выполняет требования стандарта, также является непростой задачей. Еще более сложной является
12
задача установления, насколько корректно приложение пользуется некоторым стандартным интерфейсом. В идеале хотелось бы, чтобы проверка реализации интерфейса на выполнение требования стандарта автоматически гарантировала, что любое приложение, корректно использующие данный интерфейс будет функционировать на данной реализации. Достижение этого идеала требует как развития методов спецификации требований, так и методов верификации реализаций интерфейсов и приложений. Таким образом, стандартизация интерфейсов Linux и верификация Linux являются важными инфраструктурными задачами, которые нужно решать для успешного широкого распространения Linux. Решение этих задач является необходимым условием для успешного внедрения Linux в госструктурах, так как без решения проблем переносимости приложений широкое внедрение Linux невозможно. Ниже приводится подборка материалов по растущему распространению ОС Linux в госструктурах и по проблемам тестирования и сертификации ОС Linux.
Краткая библиография по использованию Linux в госструктурах 1. USA: Linux в госструктурах http://www.linuxjournal.com/article/7932 http://www.redhat.com/en_us/USA/home/solutions/government/state/ — RedHat с новостями про использование Linux в госструктурах http://archives.cnn.com/2001/TECH/computing/01/17/linux.government.idg/index.html — колонка в CNN о Linux в госструктурах http://www.businesswire.com/webbox/bw.031901/210782363.htm — Linux в военных структурах USA http://www.larstan.net/GTSI/LinuxOpenRoad1.pdf — о преимуществах Linux для госструктур 2. Европа http://www.businessweek.com/magazine/content/04_45/b3907083_mz054.htm http://www.computerworld.com/industrytopics/financial/story/0,10801,103198,00.html http://www.govtech.net/magazine/channel_story.php/13155 — Германия http://www.eweek.com/article2/0,1895,1901495,00.asp — Швейцария http://www.theregister.co.uk/2003/06/16/linux_in_europe/ — краткий обзор по странам 3. Подборка новостей о переходе на Linux в госструктурах http://linux.quicksurf.com/index.php?cat=34 4. Корея http://www.theregister.co.uk/2002/01/14/korea_migrates_120k_civil_servants/ 5. Китай http://trends.newsforge.com/article.pl?sid=05/12/08/2034216&from=rss http://www.linuxelectrons.com/article.php/20051005183120307 http://www.infoworld.com/articles/hn/xml/02/08/13/020813hnchina.html http://www.wsws.org/articles/2000/jul2000/lin-j15.shtml 6. Индия http://trends.newsforge.com/trends/06/01/27/1644258.shtml?tid=138 http://economictimes.indiatimes.com/cms.dll/articleshow?artid=24598339 http://atulchitnis.net/writings/oss-govt.php http://sify.com/news_info/fullstory.php?id=13562974 13
7. Япония http://www.newsfactor.com/story.xhtml?story_id=01300000AQCK http://www.newsfactor.com/story.xhtml?story_id=38516 http://people.valinux.co.jp/~sado/articles/japanwantsdebian.html 8. Сайт Novell с новостями об использовании Linux в госструктурах http://www.novell.com/servlet/CRS?reference_name=&op=%25&Action=Start+Search&Submit=Start+Search&source=novl&full_text_limit=sho wcase_verbiage+%2C+press_release&MaxRows=0&&&full_text=®ion_id=0&countr y_id=0&industry_id=13 9. Австралийские госструктуры http://www.novell.com/success/comsuper.html http://www.novell.com/success/mbf.html 10. Европа, Бл. Восток и Африка http://www.novell.com/ru-ru/industries/government/emea/index.html 11. Латинская и Южная Америки http://www.novell.com/industries/government/latinamerica/index.html
Краткая библиография по тестированию и сертификацию на соответствие LSB 1. http://www.opengroup.org/lsb/cert/ — Сайт программы сертификации LSB. 2. http://www.linuxinsider.com/story/brvYrWeJ3c9diU/Worlds-Second-Linux-StandardsTesting-Lab-Settles-in-China.xhtml — открытие World's Second Linux Standards Testing Lab в Китае. 3. http://www.osdl.org/lab_activities/kernel_testing/ — Страница Проекта Linux Kernel Testing. 4. http://posixtest.sourceforge.net/ — Open POSIX Test Suite — поддерживаемый Intel проект по созданию открытого тестового набора для проверки соответствия спецификациям, функционального и стресс тестирования, а также тестирования производительности для функций, описанных в разделе System Interfaces стандарта IEEE Std 1003.1 (POSIX.1). 5. http://www.livejournal.com/users/udrepper/8511.html — Высказывания Ulrich Drepper, ведущего сотрудника Red Hat, в своем Live Journal о тестовом наборе для сертификации LSB.
14
«возврат» упущенного рынка ОС для ПК. Общий код, не являющийся чьей-либо собственностью, оставляет возможность для совместного участия участников сообщества Linux в конкурентной борьбе. Для начала, Linux начал активно занимать ниши в рынках для мэйнфреймов, крупных серверов и кластеров, отобрав эту долю у Unix.
Проблемы совместимости Linux-систем
А как обстоят дела у Linux с точки зрения его возможной фрагментации? Наличие конкурирующих дистрибутивов потенциально вызывает опасения в вероятной возможности такого исхода.
А. И. Гриневич, Д. А. Марковцев, В. В. Рубанов Институт системного программирования РАН (ИСП РАН), Б. Коммунистическая, 25, Москва, Россия E-mail: {tanur,day,vrub}@ispras.ru
Аннотация Статья рассказывает о проблемах совместимости и переносимости приложений между различными клонами Linux. Вскрываются причины такого положения дел, и дается краткое описание разнообразия современных Linux-систем. Возможным выходом из сложившейся ситуации является стандартизация интерфейсов между операционной системой и приложениями и строгое следование выработанным стандартам, что Linux-сообщество сегодня и пытается реализовать, разрабатывая и внедряя стандарт Linux Standard Base.
1. Введение Для решения инфраструктурной проблемы слабой совместимости различных версий Unix, в 1985 году в рамках организации IEEE началась работа над стандартом, обеспечивающим переносимость программного обеспечения (ПО) между ними. В результате к 1990 увидел свет стандарт IEEE 1003, также получивший название POSIX [1]. Это стандарт на программные интерфейсы (API) и команды Unix-клонов. Для игроков рынка Unix задача унификации привела к сложным политическим проблемам — ведь любое решение, любой выбор между альтернативными вариантами для достижения согласия ведёт к тому, что более стандартным признается решение одного производителя по сравнению с решением другого. В результате стандарт изобилует двусмысленными утверждениями («в данном случае возможен один из двух альтернативных вариантов поведения») и недомолвками («стандарт не специфицирует поведение функции в этом случае»). Различные попытки проанализировать причины поражения Unix в целом приводят к одной основной причине — его фрагментации. Игроки рынка Unix конкурировали не только с другими типами операционных систем (ОС), но и друг с другом, вводя частные расширения и закрытые интерфейсы, тем самым ограничивая круг возможных приложений одним клоном. Зародившаяся в начале 90-х годов система Linux, вобравшая в себя большинство кода созданного в рамках движения GNU, впитавшая основные идеи Unix, благодаря открытости и независимости, явилась универсальным компромиссом. Код реализовывался с нуля, не опираясь на какую-либо реализацию, а только на текст самого стандарта POSIX. Тем самым система получилась изначально во многом POSIX-совместимой. Независимость позволила объединить усилия различных игроков рынка Unix, в борьбе за
15
На первый взгляд, такая постановка вопроса кажется надуманной, и сама опасность выглядит довольно призрачной для человека знакомого с идеологией и философией открытого и бесплатного ПО. Фактически есть единый код, большинство «дистрибутивов» работают на основе одного и того же ядра, одних и тех же библиотек, и, тем самым во многом являются идентичной и по построению совместимой ОС. Ведь в отличие от Unix-клонов, различные версии Linux объединены не только общими интерфейсами, они имеют общую реализацию, один и тот же код. Из этого, очевидно, следует, что приложение должно сохранять работоспособность и совместимость между различными версиями Linux. Несмотря на всю очевидность, это утверждение не получило полного подтверждения практикой. Наряду с фрагментацией рынка дистрибутивов Linux по подходам и дополнительной функциональности, наблюдаются существенные перекосы в поддержке различными клонами довольно общих и стандартных приложений. Для того чтобы предотвратить печальный опыт клонов Unix в 1998 году в рамках специально созданной организации FSG [5] (Free Standards Group — консорциум по развитию открытых стандартов), началась работа над стандартом LSB [4] (Linux Standard Base — базовое семейство стандартов Linux). Благодаря усилиям со стороны организаций X/Open, IEEE и ISO, открывших стандарт POSIX и часть тестов для свободного доступа, был заложен начальный фундамент в дело стандартизации Linux. Что и зачем нужно стандартизовать? Неужели единый открытый код сам по себе не является единым и открытым стандартом? В этой статье мы попытаемся посмотреть на проблему фрагментации Linux с точки зрения архитектурного устройства дистрибутива и связей его остальных компонентов. В данной статье мы не будем останавливаться на вопросах поддержания архитектурной документации, процесса разработки отдельных компонентов и процедурах взаимодействия между группами разработчиков. Эти вопросы крайне важны и бесспорно оказывают свое влияние на сложившуюся ситуацию, но их более подробное рассмотрение потребует отдельной работы.
2. Проблемы совместимости приложений Как проявляются различия между дистрибутивами Linux на практике, и является ли эта проблема реальностью? Для ответа на этот вопрос достаточно привести простой пример. IBM является крупнейшим производителем программных решений, в том числе и под Linux. Основу коммерческих
16
предложений IBM составляют 5 линеек программных продуктов: DB2, WebSphere, Rational, Tivoli и Lotus. Практика показала, что поддержка всех пяти линеек для одного дистрибутива Linux обходится в 20 млн. долл./год. Эта стоимость включает в себя в основном затраты на разработчиков и тестировщиков, ответственных за полноценную работу и поддержку этих приложений под конкретный дистрибутив Linux. Автоматически следует, что поддерживаются только те дистрибутивы, для которых прибыль от продажи продуктов превышает эти 20 млн. Фактически получается, что продукты IBM поддерживают только дистрибутивы SuSE и RedHat. Тем самым возникает ситуация неравноправия — то, что работает на одних дистрибутивах, не хочет запускаться на других, тем самым нарушая ситуацию с открытостью и равноправием. Кроме того, поддержка дистрибутивов удорожает разработку продуктов, автоматически приводя к удорожанию ПО.
решение этой проблемы и направлены усилия консорциума FSG, представляющей интересы основных игроков Linux-рынка. В рамках этой организации разрабатывается стандарт Linux Standard Base, направленный на стандартизацию основных бинарных интерфейсов и команд различных дистрибутивов.
Известен рассказ о том, что одной из причин начала участия Intel в работе над стандартом LSB стало то, что в определенный момент времени, когда это понадобилось, не нашлось ни одного дистрибутива, на котором можно было запустить одновременно продукты Oracle и SAP, необходимые для одного из решений. В целом и тот и другой поддерживают Linux, поддерживают различные его дистрибутивы, но в нужный момент ситуация сложилась так, что поддержка пропала для комбинации необходимых версий этих программ. Скорее всего, причина была в том, что в результате наличия постоянно изменяющегося API совместимость между определёнными версиями приложений и версиями ОС Linux была утеряна. То, что работало вчера, перестало работать сегодня.
Если отвлечься от наличия конкретных дистрибутивов, то «обобщенная» модель системы на базе ОС Linux выглядит примерно так, как это изображено на Рис. 1.
3. Структура Linux Среди некоторых Linux-программистов бытует мнение, что если программа перестала работать, то, имея исходные коды, её очень легко подправить, поэтому проблемы с совместимостью никакой нет. Легко или нет сделать исправление зависит от того, что может измениться. Для того чтобы разобраться в том, что может на это повлиять, давайте подробнее рассмотрим структуру Linux.
Мы увидим совсем другую ситуацию, если взглянем на ОС Sun Solaris. Прежде всего, фирма Sun гарантирует, что программа, скомпилированная для Solaris 2.6, будет работать без перекомпиляции и под версией 10 (т.е. будут работать программы более чем десятилетней давности). Более того, если программа компилировалась тогда, то она будет компилироваться и сейчас. Разработчики Sun прилагают усилия, чтобы это было так — при каждом изменении кода прогоняется набор из более 2400 приложений различного назначения и состава для того, чтобы убедиться, что обратная совместимость не нарушилась из-за внесенных изменений. Более того, если кто-то обнаруживает, что приложение перестало работать по причине несовместимости между версиями Solaris, то Sun берёт на себя ответственность за исправление этого несоответствия. В данном случае, разработчик ОС взял на себя ответственность и расходы на поддержание обратной совместимости.
3.1. Роль ядра
В случае с Linux данная работа долгое время не велась — приложения и дистрибутивы жили своей собственной обособленной жизнью. Как правило, разработчики приложений ведут поддержку пары-тройки конкретных дистрибутивов, или разработчики дистрибутивов поддерживают ограниченный набор необходимых приложений.
ОС Linux имеет монолитное ядро [6, большую программу, внутри которой неограниченным доступом ко всем Альтернативный подход состоит
Рисунок 1. Архитектура Linux-системы.
Самым печальным в этом случае является отсутствие универсального способа написания программ, гарантированно обеспечивающего их переносимость. На
17
18
7], т.е. ее ядро представляет собой единую все функциональные компоненты обладают его внутренним структурам и функциям. в том, чтобы иметь микроядро, в
котором все функциональные компоненты разделены на различные модули с хорошо определенными интерфейсами и протоколами взаимодействия.
реализуют большее число интерфейсов, чем 1532 функции, определенные в LSB Core.
В Linux термин «модуль» имеет иное значение — это динамически подгружаемый вспомогательный элемент ядра. В основном модули ядра это драйверы устройств, драйверы файловой системы и сетевых протоколов.
Функции системных библиотек можно классифицировать следующим образом по степени взаимодействия с ядром Linux.
Итак, ОС Linux содержит монолитное ядро с поддержкой динамически загружаемых модулей. Модули могут загружаться и выгружаться как явно (с помощью команд insmod и rmmod), так и неявно, когда демон ядра kerneld загружает и выгружает модули автоматически по мере надобности. Динамическая загрузка/выгрузка, очевидно, позволяет сокращать используемую ядром память. Тем не менее, ничего не бывает бесплатно, и за использование модулей всё-таки придется заплатить — как небольшим падением производительности, так и чуть большим использованием памяти, поскольку модульная организация подразумевает дополнительные структуры с описанием модуля. После того, как модуль загружен, он эквивалентен в правах с любым другим кодом ядра. Он обладает теми же правами и ограничениями, как и любой другой код ядра, или, если взглянуть с другой стороны, модули ядра могут привести к падению или внештатной ситуации, как и остальной код ядра или драйвер устройства. Наряду с возможностью «повесить» ОС из-за того, что модуль некачественно реализован, существует ещё одна опасность. Что происходит, если загружаемый модуль создан для более ранней или более поздней версии ядра, чем та, которая его загружает? Это может привести к проблеме, если модуль, обращается к некоторой процедуре ядра и передает неправильные аргументы вызова. Опционально, в ядре присутствует возможность проверки версии загружаемого модуля перед загрузкой. Почему же все-таки ядро монолитно? Почему не переделать его как микроядро? Опыт показал, что микроядра обладают худшей производительностью по сравнению с монолитными ядрами. В идее микроядра заложена архитектурная проблема — различные компоненты ядра не могут взаимодействовать между собой без преодоления барьеров безопасности, что требует времени и ресурсов.
3.2. Роль системных библиотек Каждая конкретная Linux система создается для работы одного или нескольких приложений. Однако, кода самого приложения недостаточно, чтобы извлечь необходимый пользователям сервис из аппаратуры. Большинство приложений использует в своей работе обращения к функциям библиотек. Стандарт LSB Core 3.1 определяет следующие системные библиотеки: libc, libcrypt, libdl, libm, libpthread, librt, libutil, libpam, libz, libncurses. В современных Linux системах интерфейсы этих системных библиотек реализуются библиотеками glibc [9], Linux-PAM [10], zlib [11] и ncurses [12], которые на самом деле
19
Реализация функции полностью содержится в библиотеке и ядро не используется (например strcpy, tsearch).
В библиотеке реализуется тривиальная “обертка” (wrapper) для вызова соответствующего интерфейса ядра (например read, write).
Реализация функции содержит как вызовы системных интерфейсов ядра (причем возможно нескольких разных), так и часть кода в самой библиотеке (например pthread_create, pthread_cancel).
Само ядро Linux содержит огромное количество экспортируемых точек входа, однако подавляющее большинство из них составляет внутренний интерфейс для использования модулями и подсистемами самого ядра. Внешний интерфейс содержит порядка 250 функций (версия 2.6). Из них, к примеру, в своей реализации библиотека GLIBC 2.3.5 использует 137. Таким образом, то, с чем взаимодействует пользовательское приложение в основном реализовано не в ядре ОС, а в библиотеках поддержки – подавляющая часть функциональности обеспечивается внешними библиотеками. Кстати, именно поэтому в свое время Ричард Столлман настоял на том, чтобы дистрибутивы Linux именовались как GNU/Linux. Большая часть исходного кода и функциональности реализовывается не разработчиками ядра, а библиотеками и утилитами, сделанными в рамках проекта GNU.
3.3. Роль этапа сборки Может показаться, что для надежности системы на уровне поведения интерфейсов системных библиотек все Linux системы одинаковы, и достаточно, если тестирование будет проводиться разработчиками ядра и библиотек. Однако это не совсем верно. Уже на уровне интерфейсов системных библиотек существует масса измерений, которые делают практически каждую Linux систему уникальной с точки зрения проверки качества. Поведение интерфейсов для приложений определяется комбинацией из библиотек, ядра и аппаратуры. В свою очередь ядро и библиотеки определяются своей версией (включая официальные или неофициальные патчи и модификации) и, что немало важно, конфигурацией сборки. Дело в том, что при сборке одной и той же версии ядра или библиотеки можно задавать массу параметров, которые делают понятие версии расплывчатым. Одна и та же версия ядра или библиотеки, собранные при разных значениях конфигурационных параметров, могут работать по-разному. Таким образом, для обеспечения надежности каждой специфической конфигурации Linux системы необходимо ее отдельное тестирование. Многое может пояснить процедура сборки ядра. Проект Linux From Scratch [8] содержит последовательность шагов необходимых для сборки дистрибутива
20
Linux «с нуля». Вот упрощённая последовательность шагов для сборки дистрибутива LFS Linux версии 6.0:
системы может оказаться невозможной из-за отсутствия или изменения какой-либо функции, либо усложнена.
Сборка бинарных утилит
Сборка многих компонентов требует дополнительных действий. К примеру, инструкция по сборке flex для данного дистрибутива содержит замечание:
1. 2. 3. 4. 5. 6. 7. 8. 9.
Binutils-2.15.94.0.2.2 Pass 1 GCC-3.4.3 - Pass 1 Linux-Libc-Headers2.6.11.2 Glibc-2.3.4 Adjusting the Toolchain Tcl-8.4.9 Expect-5.43.0 DejaGNU-1.4.4 GCC-3.4.3 - Pass 2
10. Binutils-2.15.94.0.2.2 Pass 2 11. Gawk-3.1.4 12. Coreutils-5.2.1 13. Bzip2-1.0.3 14. Gzip-1.3.5 15. Diffutils-2.8.1 16. Findutils-4.2.23 17. Make-3.80 18. Grep-2.5.1a 19. Sed-4.1.4
20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30.
Gettext-0.14.3 Ncurses-5.4 Patch-2.5.4 Tar-1.15.1 Texinfo-4.8 Bash-3.0 M4-1.4.3 Bison-2.0 Flex-2.5.31 Util-linux-2.12q Perl-5.8.6
71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88.
E2fsprogs-1.37 Grep-2.5.1a GRUB-0.96 Gzip-1.3.5 Hotplug-2004_09_23 Man-1.5p Make-3.80 Module-Init-Tools-3.1 Patch-2.5.4 Procps-3.2.5 Psmisc-21.6 Shadow-4.0.9 Sysklogd-1.4.1 Sysvinit-2.86 Tar-1.15.1 Udev-056 Util-linux-2.12q Конфигурация загрузки
Flex contains several known bugs. These can be fixed with the following patch: patch -Np1 -i ../flex-2.5.31-debian_fixes-3.patch Особо хочется отметить, что в процесс сборки включается сборка средств компиляции, которые также претерпевают существенные изменения во времени. Даже базовые компоненты Linux нередко оказываются устаревшими. Так, например, версия компилятора gcc 4.0.0 не годится для сборки ядра 2.6.11 (хотя они современники) и требует использования специального патча для устранения этой несовместимости.
Файловая система и доводка утилит 31. Entering the Chroot Environment 32. Changing Ownership 33. Creating Directories 34. Creating Essential Symlinks 35. Creating the passwd, group, and log Files 36. Populating /dev 37. Linux-Libc-Headers2.6.11.2 38. Man-pages-2.01 39. Glibc-2.3.4 40. Re-adjusting the Toolchain 41. Binutils-2.15.94.0.2.2 42. GCC-3.4.3 43. Coreutils-5.2.1 44. Zlib-1.2.2 45. Mktemp-1.5 46. Iana-Etc-1.04 47. Findutils-4.2.23 48. Gawk-3.1.4
49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70.
Ncurses-5.4 Readline-5.0 Vim-6.3 M4-1.4.3 Bison-2.0 Less-382 Groff-1.19.1 Sed-4.1.4 Flex-2.5.31 Gettext-0.14.3 Inetutils-1.4.2 IPRoute2-2.6.11050330 Perl-5.8.6 Texinfo-4.8 Autoconf-2.59 Automake-1.9.5 Bash-3.0 File-4.13 Libtool-1.5.14 Bzip2-1.0.3 Diffutils-2.8.1 Kbd-1.12
89. Linux-2.6.11.12 –
Рисунок 2. Эволюция библиотек и состав дистрибутивов.
Ядро
3.3.1. Конфигурации
Отметим, что сборка ядра осуществляется в самом конце (на самом последнем, 89-м шаге) с помощью собранных перед этим бинарных утилит. Важно учитывать версии приведённого в каждом элементе списка компонента. Замена одной версии компонента на другую не всегда тривиальна — сборка
21
Под конфигурацией системной части дистрибутива мы понимаем комбинацию версии ядра (включая отдельные патчи), версий системных библиотек, параметров их сборки и архитектуры, на которой это все работает.
22
На Рисунке 2 изображена конфигурация сборки двух гипотетических дистрибутивов. Каждый из них есть совокупность версий компонентов и патчей. Между версиями компонентов добавляется новая функциональность, а также убираются морально устаревшие интерфейсы и функции.
4.1. Red Hat
Например, на данной диаграмме легко видеть, что поскольку дистрибутивы 1 и 2 используют различные версии GCC, совместимость по исходным кодам между ними отчасти утеряна — не всё что собиралось с помощью gcc 3.4 может быть собрано с помощью gcc 4.0 без доработки.
Компания Red Hat, Inc. [14] представляет, наверное, самые известные дистрибутивы в мире. Это Red Hat Enterprise Linux, коммерческий дистрибутив, стабильный и надёжный, поставляемый с услугами сервиса и поддержки для промышленных потребителей. И Fedora Core (дословный перевод — фетровая шляпа) — спонсируемый Red Hat открытый дистрибутив, разрабатываемый при помощи сообщества. Fedora Core используется также и как тестовый полигон — обкатанные на этом дистрибутиве элементы позже добавляются в Red Had Enterprise.
4. Основные дистрибутивы Linux
4.2. Debian
С точки зрения прикладного ПО дистрибутив есть конфигурация базовых компонентов Linux. По адресу [13] можно найти перечень известных дистрибутивов Linux. На момент написания статьи их было 508. И это только публично доступные, открытые для широкой публики дистрибутивы. Здесь не учитываются непубличные версии, сделанные для внутреннего применения в различных компаниях, оборонных ведомствах и т.п. Модифицировать и распространять код позволяет GNU-лицензия. Из этого следует, что можно взять произвольный дистрибутив, внести в него модификации (как минимум в его компоненты, подпадающие под GNU или подобную лицензию) и распространять далее. Это и происходит на практике — различные дистрибутивы «наследуют» основные свойства своих предков, добавляя минимум специфических свойств (локализация, определенный набор прикладного ПО, программа установки, диалоги конфигурации, графические элементы и т.п.). Дистрибутивы можно разделить по следующим категориям: 1. По базовым производителям. К примеру, RedHat, Slackware, SuSE, Debian, Asianux, Mandriva являются основными «ветвями» Linux-индустрии. Эти дистрибутивы не являются наследниками других. Их можно считать стратегическими направлениями развития в Linux вообще. Большинство остальных дистрибутивов явно принадлежат к одной из упомянутых ветвей – в основном наследуя исходный код и приложения и добавляя специфическую функциональность. 2. По локализации. Во многих странах присутствует локальный производитель Linux (скажем, в России всем известны дистрибутивы ASP Linux и ALT Linux). 3. Встроенные версии. Дистрибутивы для встроенного применения в мобильных устройствах; дистрибутивы, работающие без поддержки файловой системы; облегченные версии для использования в КПК; переносные версии для запуска с ограниченных носителей (Linux на дискете, Linux на CD и т.п.). 4. Специализированные версии с поддержкой определенной аппаратной архитектуры (AlphaLinux с поддержкой Alpha, ARM Linux с поддержкой ARM и т.п.).
23
Вот цитата из Wikipedia, коротко характеризующая семейство Debian [15]. «Debian — наиболее строгий из всех дистрибутивов в отношении лицензий программ. Имеет наибольшее хранилище пакетов — готовых к использованию программ, — и если даже не по их числу, то по числу поддерживаемых архитектур: начиная с ARM, используемой во встраиваемых устройствах, наиболее популярных x86 и PowerPC, новых 64-разрядных AMD и заканчивая IBM S/390, используемой в мэйнфреймах. Хранилище разделено на три ветки:
стабильную (stable), содержащую пакеты, вошедшие в последний официальный дистрибутив (обновление пакетов в нём происходит только для устранения уязвимостей); тестируемую (testing), из которой будет формироваться следующий стабильный дистрибутив; нестабильную (unstable), в которой пакеты готовятся к помещению в тестируемую ветку.
Существует также ветка, называемая экспериментальной (experimental); в неё помещаются пакеты, претерпевающие особо большие изменения. В Debian серьёзно относятся к любым изменениям, благодаря чему проблемы с обновлением бывают очень редко (ценой этому является не совсем быстрая реакция на выход новых версий программ). Для работы с хранилищем разработаны разные средства, самое популярное из которых — APT. Debian стал основой целого ряда других дистрибутивов (более 100, см. список дистрибутивов, основанных на Debian). Самые известные из них — Adamantix, Bioknoppix, Clusterix, Gnoppix, Knoppix, Kubuntu, Libranet, Linspire, MEPIS, Ubuntu Linux и Xandros Desktop OS.
4.3. Slackware Целями, положенными в основу построения этого дистрибутива [16], являются простота и стабильность. Авторы придерживаются базового принципа KISS (Keep it simple, stupid — Не усложняй) — это относится, прежде всего, к простоте построения системы, а не к простоте использования. Slackware использует стартовые скрипты стиля BSD, в то время как большинство других дистрибутивов использует стиль System V.
24
тестов для учета эволюции стандарта или специальных требований пользователя. Эти факторы обеспечивают ценность тестового набора Центра в качестве важного дополнения к существующим тестам Linux.
4.4. SuSE Название «S.u.S.E.», позднее сокращённое до «SuSE», является акронимом немецкой фразы «Software- und System-Entwicklung» («Программная и системная разработка») [17] Дистрибутивы SUSE ориентированы в первую очередь на настольные компьютеры, хотя также доступен ряд продуктов класса предприятия, таких как SUSE Linux Enterprise Server (SLES). Про эти дистрибутивы было написано немало положительных обзоров за их инсталлятор и систему настройки YaST, разработанные программистами SUSE. Документация, идущая с коробочными версиями, постоянно отмечается как наиболее полная, детальная и удобная.
Проект тестового набора OLVER, разрабатываемого в рамках созданного на базе ИСП РАН Центра верификации Linux [3], является инициативой, нацеленной на повышение качества тестов для Linux и общего покрытия ими интерфейсов стандарта. Главные цели проекта OLVER следующие:
Проанализировать стандарт Linux Standard Base (LSB) Core 3.1 (ISO/IEC 233601) с целью выявления атомарных требований к поведению основных системных библиотек (секции III. Base Libraries и IV. Utility Libraries — всего 1532 функции).
Разработать каталог требований LSB Core3.1 для 1532 интерфейсных функций Linux. В процессе работы обнаружить неточности и ошибки в тексте стандарта LSB и связанных с ним стандартов и сообщать их оригинальным разработчикам для внесения изменений в будущие версии.
Разработать открытый тестовый набор для функционального тестирования различных Linux систем на соответствие требованиям стандарта LSB Core в отношении поведения системных интерфейсов прикладного программирования Linux.
Разработать систему конфигурации и запуска тестов. Задача такой системы заключается в предоставлении удобных средств для настройки тестов по ряду параметров (например, глубина тестирования, опции целевой системы, выбор варианта поведения из нескольких, допустимых стандартом) и визуализации результатов тестирования с указанием на расхождения с конкретными требованиями стандарта.
4.5. Asianux Asianux [18] — объединенный продукт, включивший в себя японский Miracle Linux и китайский Red Flag Linux. Позднее к проекту присоединилась корейская компания Haansoft Inc. Производимый в китайском центре разработок фирмы Oracle в Пекине, Asianux — это совместная попытка основать стандартизованный общий Азиатский Linux, определяющий ядро, набор библиотек и пакетов. Объединение вокруг Asianux также является естественным центром сертификации ПО и оборудования для работы с производными этой системы.
5. Решение проблемы: LSB LSB (Linux Standard Base) — это основной современный стандарт, определяющий требования совместимости к Linux-системам. Основную часть стандарта (LSB Core) составляют требования на системные интерфейсы, которые должны поддерживаться всеми дистрибутивами Linux. В этой части LSB во многом ссылается на стандарт POSIX. Разработкой и развитием стандарта LSB занимается организация Free Standards Group. Подробные сведения о LSB можно найти на сайте http://www.freestandards.org/en/LSB. Однако, даже очень хорошие стандарты остаются лишь благими пожеланиями, пока нет удобных и надежных способов проверить формальное соответствие им. Дополнение стандартов формальными описаниями позволяет выявить нечеткие места, устранить ещё встречающиеся противоречия и учесть неявные требования, которые часто упускаются даже разработчиками официальных сертификационных тестов. Анализ существующих тестов показывает их слишком малый охват — многие функции не тестируются вообще, а многие тестируются только в отношении базовых требований стандарта, не проверяя тонкие аспекты. Подход к тестированию с помощью систематической формализации требований, используемый в Центре верификации ОС Linux, позволяет досконально учесть все принципиально проверяемые требования стандарта. Кроме того, автоматическая генерация тестов обеспечивает полный перебор различных ситуаций в работе тестируемой системы, а также облегчает модификацию 25
В рамках проекта OLVER текстовые описания существующих стандартов дополняются формальными описаниями в виде спецификаций на языке SeC (Specification Extention of C) — спецификационном расширении языка С. В процессе формализации выявляются нечеткие места и противоречия, а также учитываются неявные требования. Найденные замечания к текстовой версии стандартов сообщаются соответствующим организациям, отвечающим за их развитие (Free Standards Group по LSB, Austin Group по POSIX). Результаты работы Центра (готовые тестовые наборы из состава OLVER и инструменты их генерации и эксплуатации) будут публиковаться на сайте Центра и распространяться по свободной лицензии Apache License 2.0.
6. Заключение Масштабность объекта, коим является ОС Linux и количество параметров сборки любого ее дистрибутива приводят к плохо совместимым между собой продуктам. Общедоступность и открытость кода сами по себе это не являются панацеей — получаемые в результате дистрибутивы обладают существенными
26
различиями даже на уровне базовых компонентов, рассмотренных в этой статье. Это создает проблемы для разработчиков прикладного ПО, поскольку повышает трудоемкость и стоимость его разработки. Для того чтобы предотвратить печальные последствия, уже имевшие место для Unixсистем, необходимы меры обеспечения кросс-совместимости дистрибутивов. Переносимость приложений позволит утвердить Linux как единую платформу и заметно снизить стоимость разработки и поддержки приложений, что должно положительно сказаться на их количестве. На сегодняшний день основной инициативой по обеспечению переносимости и совместимости между Linux-системами является стандарт LSB (Linux Standard Base). Этот стандарт поддерживается основными производителями дистрибутивов (RedHat, SuSe, Mandriva) и уже можно считать, что заложены первые камни в фундамент единой, не фрагментированной платформы Linux, обеспечивающей переносимость приложений, как на основе исходного кода, так и в бинарном виде. Известно, что интерфейсный стандарт хорош настолько, насколько хороши тестовые набор, проверяющие соответствие ему и позволяющие разработчикам как различных версий ОС, так и приложений убедиться, что у них не будет проблем с совместимостью. К сожалению, в настоящий момент тестовый набор, используемый для проверки соответствия дистрибутива LSB, покрывает менее 20% описываемых стандартом интерфейсов. Разработка тестов, обеспечивающих качественную проверку стандарта, в настоящий момент является одним из ключевых шагов к общему успеху, поэтому такие проекты как OLVER необходимы для гарантирования стабильного развития экосистемы Linux в целом.
Литература [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18]
IEEE POSIX® Certification Authority. http://standards.ieee.org/regauth/posix/ Wikipedia: Unix Wars. http://en.wikipedia.org/wiki/Unix_wars Центр верификации ОС Linux. http://www.linuxtesting.ru LSB (Linux Standard Base). http://www.freestandards.org/en/LSB FSG (Free Standards Group). http://www.freestandards.org Основной сайт разработчиков ядра Linux. http://www.kernel.org/ David A. Rusling. The Linux Kernel book. http://en.tldp.org/LDP/tlk/modules/modules.html LFS: Linux From Scratch. http://www.linuxfromscratch.org glibc. http://www.gnu.org/software/libc/libc.html Linux-PAM. http://sourceforge.net/projects/pam zlib. http://www.zlib.net ncurses. http://invisible-island.net/ncurses/ Перечень дистрибутивов Linux. http://lwn.net/Distributions/ RedHat. http://www.redhat.com Debian. http://www.debian.org Slackware. http://www.slackware.com SuSE. http://www.suse.com Asianux. http://www.asianux.com
27
! , % .
Linux Standard Base: ? . .
( ), . , 25, ,
E-mail:
[email protected]
, Linux Standard Base,
, . . , 10- ,
,
, .
RedHat
SuSe
Mandriva
Debian
Ubuntu
Asianux
1. Linux
1. , ! Linux
Linux. " # $ ! . Linux %
, %
, . &
,
.
) ! # ! ! Linux, , ,
. x
29
o %
;
o .
' , ! Linux, % ,
. &
% $ ! , " ( 1. ) % " % . * % " , " IBM, Oracle SAP. IBM
Linux RedHat Novell SuSe, Oracle RedHat Enterprise Linux, SuSe Linux Enterprise Server Asianux, SAP #! RedHat Enterprise Linux, SuSe Linux Enterprise Server Red Flag Advanced Server. ) , $
% % $ % % . & # % , % $
,
x , o ! ; o % . x , o %
% ; o Linux # % . * , 1998 Linux + % % $ . * %
30
# ! UNIX . / ! % % ! . ) ! . ! % #% , " # . /
, % " % ! , % .
, , "
( 2. , $ ! , .
, % % % ! , . % : x
! %, % ;
x
! .
x
, $ !% .
x
4
, ! % % .
x
%,
# , ! , .
* , , $ Linux,
! ! .
2. / ! UNIX % Bell Telephone Laboratories, AT&T, $% ! 60-%
$ . * 1970 $ PDP-7. 1971 ! $ PDP-11, 1973
4. & %
$ % , $% $
! UNIX.
Linux Standard Base
UNIX # 4<= , AT&T UNIX. AT&T ! , 1974
% ! . RedHat
SuSe
Mandriva
Debian
Ubuntu
Asianux …
2. Linux Standard Base .
) , %
! . "
31
/ ! $ UNIX # . " # % $
! UNIX.
$ ! . & ! , , ! UNIX
UNIX- % !% . ? !
,
32
$ UNIX- , $ $ % !. * UNIX 1980 ! % ! /usr/group, + $ % UNIX- % $ ! $
% . (
1984 !#!, # , ! . & !#!, /usr/group Standard1, $ ,
. - %, ANSI ! 4
. -%, % /usr/group, $ Portable Application Standards Committee (PASC), " IEEE CS 1984 . PASC Project.1003. *
IEEE Trial-Use Standard, 1986 . ) IEEE-IX, IEEE’s UNIX,
Portable Operating System Interface for Computing Environment ( POSIX), % . * #! IEEE Std 1003.1 ( POSIX 1003.1) $ 1988 . #! # ! C.
% % ,
1990 ! IEEE Std 1003.1-1990,
ISO/IEC 9945-1:1990. * ! #! # ! PASC IEEE Std 1003.2-1992 (POSIX 1003.2), ISO/IEC 9945-2:1993. * IEEE Std 1003.1-1990 PASC % . 4
% : IEEE Std 1003.1b-1993 Realtime Extension IEEE Std 1003.1c-1995 Threads IEEE Std 1003.1d-1999 Additional Realtime Extension IEEE Std 1003.1i-1995 Technical Corrigenda to Realtime Extension IEEE Std 1003.1j-2000 Advanced Realtime Extension
IEEE Std 1003.1q-2000 Tracing 1996 ! IEEE Std 1003.1-1996, $ IEEE Std 1003.1b-1993, IEEE Std 1003.1c-1995 IEEE Std 1003.1i-1995, . & !
ISO/IEC 9945-1:1996. 1003 IEEE (POSIX), 1984 % ! X/Open. Q ! # ! , % ! ! % , ! . / POSIX ! % UNIX- % , X/Open . $ UNIX- % . ( " X/Open Portability Guide (XPG). * XPG1 1985 # System V release 2 BSD 4.22. 4 XPG2 1987 . Q $ !#! #
% (curses). XPG3, 1990 , # X Window System, Z %. XPG4 $ 1992 , , ! ANSI 4 1989 . ' , XPG2, XPG3 XPG4 !, . POSIX XPG ! # UNIX- % !% . " ! UNIX AT&T. 1985 System V Interface Definition (SVID), !#! # System V release 2 /usr/group Standard. SVID2 SVID3, $ $ , !#! System V release 3 System V release 4 . SVID4 Novell, $ AT&T
2
1
) " UniForum 1984 Draft Standard UDS 84, UniForum — "
/usr/group. 33
System V — ! UNIX- ! AT&T. BSD (Berkeley Software Distribution) — UNIX- ! , University of California, Berkeley. & UNIX- % 90-% $ . 34
Unix System Labs UNIX $ UNIX- ! UnixWare.
! + , % 2008 .
/ SVID, AT&T ! UNIX ! . Q #! UNIX AT&T $ Sun Microsystems Microsoft System V release 4, % + % System V release 3, SunOS (Sun) Xenix (Microsoft).
* #! UNIX- % .
$
! # !% POSIX Single UNIX Specification, + $
" The Open Group, IEEE ISO/IEC. & $ !% , .
) ! AT&T % UNIX % (Apollo Computers, Groupe Bull, Digital Equipment Corporation, Hewlett-Packard, IBM, Nixdorf Computer Siemens AG) ! Open Software Foundation (OSF). * " OSF ! # !
, OSF/1 (1990). 4 % OSF OSF DCE (Distributed Computing Environment) OSF CDE (Common Desktop Environment).
, ! ,
% % # 4. ) % !% %
!. *" ! , UNIX,
POSIX.
$ UNIX- % !% + X/Open OSF The Open Group 1993 . )
The Open Group Novell UNIX. "%
The Open Group !#! UNIX (Single UNIX Specification, SUS). XPG4, $ IEEE Std 1003.1-1990, SVID3 (Level 1) OSF Application Environment Specification. " # , % 50 , 130 #!,
%
, % $ UNIX- % % BSD. "% #! $ #! #! BSD 4.3. Single UNIX Specification, $ $ 1997 , $
#! # $ , % , IEEE Std 1003.1-1996. ' $ 1998 , The Austin Group –
IEEE Std 1003, ISO/IEC 9945 Single UNIX Specification. ( "
, % "% IEEE Std 1003.1-2001, ISO/IEC 9945:2001 Single UNIX Specification version 3. " +
# ! , $
% 1003.1 1003.2. 2003 2004 % The Austin Group ,
# .
% The Austin Group
35
? POSIX
UNIX- ! %
. Q % # ! , # % . ' , ! % , "
,
. [ , # ! . " ,
! %
# . % , % OSF/1, .
$ "%
!% . ), OSF/1 , Kendall Square Research Digital Equipment Corporation, % . % + #. - %,
! . -%, ! , " % #! % % $ , % % !%.
3. Linux 4 ! Linux POSIX- ! $ % . )
, " Linux
36
# ! . POSIX ! . - %, # !#!% POSIX
! % . Q
, , % # POSIX, #
# , #!, POSIX. * ! # # . , X Window System % X/Open Common Desktop Environment % OSF, The Open Group. $ $ , , , Linux. ) #! #, #!. -%, % !
. & $
% ,
. .
% . ) ! Linux $ ! ,
% . / % %,
% ! , ! . * , $ Linux
, ! % # ! . ? " 1998 ! Free Standards Group, " Linux Standard Base (LSB). ? ] ) , ^ * , & (, Linux, , Linux International, ? _ FreeBSD [LSB98]. 4 LSB ! Linux ! % LSB- % !% . )
, ! LSB POSIX, $ ! # . , $ % !
. )
, " .
37
!
% , % ! " ! . , $ POSIX % $ % #! - , % !
. &
IEEE Std 1003.2 % % , tar cpio,
# $
#! . /, LSB !#! % #
! % # , LSB # % ! Linux. ? " ! $
, LSB ,
Linux. %
# , , % RedHat, SuSe, Mandriva, Asianux, Debian, Ubuntu. '% LSB ? ( " LSB 3.1.
3.1. Linux Standard Base 3.1 ] , # , %
, . , $ LSB ,
#. 4 " #, , LSB
% % , ! . , !
#, , « !#! LSB». '
, # , LSB, !# #. ) ,
LSB # % % %: !#! LSB !#! LSB " #. 4 LSB 3.1 7 #: AMD64, IA32, IA64, PPC32, PPC64, S390 S390X. AMD64 AMD64 Architecture [AMD64], IA32 — IA-32 Intel Architecture [IA32], IA64 – Intel Itanium Architecture [IA64], PPC32 — PowerPC Microprocessor Architecture [PPC32], PPC64 — 64-bit PowerPC Microprocessor Architecture [PPC64], S390 – IBM S/390 Architecture [S390] S390X — IBM z/Architecture [S390X]. 4 LSB 3.1 % : LSB 3.1 Core, LSB 3.1 C++ LSB 3.1 Desktop. ' , , , LSB Qt 4. (
.
38
) LSB 3.1 Core
: x ; x # % #; x # ; x ; x # ; x ! !!; x
; x ! . % % " # % C, $ #! .. ^ $ % #-% % LSB. . ! % # # Executable And Linking Format (ELF) [ELF] % , #% LSB. LSB % " : x % ! ELF #; x # #! DWARF 2.0; x % ; x ! . ) # ! % " . 4 12 % : x - % (ld-lsb.so); x 4 (lib); x % #! (libm); x POSIX (libpthread); x gcc (libgcc_s); x % (libdl); x #! (librt); x #! # (libcrypt); x #! (libpam); x #! % (libz); x #! # (libncurses); x % #! (libutil). ' , % % .
: 39
X Y ( Z)
" #!
. X # , % % ( , libdl.so.2) % ( , libc.so.6.1 IA64 libc.so.6 % % ). ^ Y ( , printf) #!
% . Z ( , GLIBC_2.2) #! , % % glibc, % % $ . % , IEEE Std 1003.1 (POSIX): * # X # Y " # #!
. ( LSB #!, % , - % #! !#!. . / #! , Linux- %,
, LSB LSB % . LSB 3.1 Core 1532 #! . 4 !
#! "% . ISO C99 [ISOC99] Large File System [LFS} LSB [LSBC31] SUS-CURSES [SUSX] POSIX 2004 [POSIX] SUSv2 [SUSv2]] SVID.3 [SVID3] SVID.4 [SVID4]
41 24 229 275 905 10 40 8
2,68% 1,57% 14,95% 17,95% 59,07% 0,65% 2,61% 0,52%
1. ! LSB 3.1.
40
, % # % , #! % , #! % % % %. ? , POSIX , # # , " POSIX- . ' " ! . ? LSB, , " # . LSB % #! 5 133 . * IEEE Std 1003.1 (POSIX), %% %% . LSB
Filesystem Hierarchy Standard (FHS) 2.3 [FHS]. FHS 2.3 LSB # /dev/null, /dev/zero /dev/tty, ! % /etc. ) LSB , #!% # /etc
, Linux Assigned Names and Numbers Authority (LANANA) [LANANA], , , % , DNS , . !!% , $ . & : x x x
x
# , !! ; (run levels); # % !!% ( : $local_fs, $network, $remote_fs); % #! ! !!% %.
" LSB % %
, % %, ! % %
.
41
" "
!/ !
. ? " ! LSB # #, # RPMv3 (Red Hat Package Manager), Red Hat, Inc [RPM30]. * " LSB
rpm % RPM, LSB- ! % % . ' , LSB RPM, % #%, , LSB- . LSB 3.1 C++ % : x x x
C++ %;
#; # % .
) C++ % C++ $. * # ,
% # C++
+ . / !, # % C++ #! 1942 % C++. LSB 3.1 Desktop # ,
% %
. "% %: x x x x x x x x
# (libX11, libSM, libICE, libXt, libXext, libXi); OpenGL (libGL); PNG (libpng12); JPEG (libjpeg); # $# (libfontconfig); GTK+ (libglib-2.0, libgobject-2.0, libgmodule-2.0, libatk-1.0, libpango-1.0, libpangoxft-1.0, libpangoft2-1.0, libgdk_pixbuf-2.0, libgdk_pixbuf_xlib2.0, libgdk-x11-2.0, libgtk-x11-2.0); Qt3 (libqt-mt); XML2 (libxml2).
% $ 19103 % . ) LSB 3.1 Desktop % 3 , # $#. Z
LSB. ), " " , .
42
3.2. Linux Standard Base ( LSB 1998 , 1999
LSB 0.1. * % 29 2001 #! — LSB 1.0. & , + LSB Core $ LSB Desktop. *
% — IA32. * # 1.0 15 , % 3040 #!: libc libm libpthread libdl libcrypt librt libz libncurses libutil libX11 libXt libGL libXext libICE libSM :
! " # # $& #
& '# (& ! ) ( * & ( # '# X Windows '# X Toolkit '# OpenGL '# + X Windows X Inter-Client Exchange (ICE) Protocol ;
710 273 75 5 3 23 40 268 6 720 304 451
LSB 1.3, 17 2002 , $ % (IA32, IA64, PPC32, S390, S390X) ! % . ^ librt , % -,
, ! Linux. : libpam ( #!) libgcc_s (
gcc). LSB 2.0, 31 2004 , # C++: libstdc++. ? , : LSB Core, LSB C++ LSB Graphics. 2.1 11 2005 $
, $ # libc, libm, libpthread libz. 4 LSB 3.0 6 2005 . *
LSB IEEE Std 1003.1 (POSIX). 4 % % # C++, gcc 3.4.4,
libXi LSB Graphics LSB Core librt. librt #!, #! , . = #! % - - LSB 3.0. libc #! duplocale, freelocale, newlocale, uselocale, getgrouplist, posix_openpt, getlogin_r. * % – #! ! , $ . #! $ #!, POSIX, % % % Linux.
76 49 37 3040
2. , # LSB .
1.1 22 2002 . libc, 68 % #!. ^ $ % #! RPC (Remote Procedure Call). ' , libpthread #! pshared ,
librt — #! , libm — #! clog. / # % 22 #!, 14 %
LSB 1.2, 28 2002 .
1.2 % (PPC32). ) " # $ 6 #!.
1.2 FSG 2002 + #! .
43
#! libpthread. % $
#! POSIX: pthread_attr_getinheritsched, pthread_attr_getschedpolicy, pthread_attr_getscope, pthread_attr_setscope, pthread_attr_setschedpolicy, pthread_getschedparam, pthread_setschedparam, pthread_attr_setinheritsched, pthread_setschedprio.
LSB 3.0 ed, logger, lp, mailx, pax, cd, getopts, read, umask wait. & LSB POSIX. Q LSB 3.0 % %, % #! # . glibc
%
! PowerPC, 44
mcontext_t, ucontext_t. " #!
(__sigsetjmp, _longjmp, _setjmp, longjmp, siglongjmp, getcontext, setcontext, swapcontext), % ucontext_t, GLIBC_2.3.4. & LSB 3.0: !#!% LSB 3.0 # PPC32 PPC64 % ,
% $ , GLIBC_2.3.4.
LSB 3.1 : LSB 3.1 Core 27 2005 , LSB 3.1 C++ LSB 3.1 Desktop — 24 2006 . ) $
LSB Core ISO. * " LSB 2.0, 2005 " ! $ $ LSB 3.1 Core ISO/IEC 23360. 4 % LSB 3.1 $ % # , % # # . & $ LSB Graphics LSB Desktop. * ! LSB Qt 4 , Qt, Linux $ " , Qt 4 . Z LSB 3.1, . )
$ , LSB $ .
3.3.
Linux Standard Base ? $ LSB 3.2 2007 4.0 2008 . LSB 3.2 : x x x
SIOCGIFCONF, SIOCGIFADDR, SIOCSIFDSTADDR, SIOCGIFNETMASK,
#
$ ! POSIX; freedesktop.org LSB Desktop;; ! %
(printing) (accessibility).
SIOCSIFNAME,
^ $ % FSG % % . ,
OpenPrinting # PAPI (Printing API) . , " # LSB 3.2 , ! % % Linux.
$ Linux, MacOS
CUPS (Common UNIX Print System). , CUPS Solaris, AIX % % BSD . *" ! # CUPS % . Q ! " ! LSB 3.2 , % ppd #,
. , $
"% % %. *
Linux # % # % : libXcomposite, libXdamage, libXfixes, libXrender, libXft. 4.0 % % . / , " # , % Linux (RHEL 6 SLES 11).
, LSB Workgroup Z , : x x x x
SIOCSIFLINK,
45
SIOCSIFFLAGS, SIOCGIFDSTADDR, SIOCSIFBRDADDR,
* freedesktop.org
! # ,
# Linux: KDE GNOME. % LSB 3.2 $ " , #! “ ”. $ LSB $ freedesktop.org.
% ! LSB POSIX (mq_*), #! POSIX Advisory Information POSIX Spawn Interfaces (pthread_spawn_*). '
, % # #! ioctl . ' ( $# # ): SIOCGIFNAME,
SIOCGIFFLAGS, SIOCSIFADDR, SIOCGIFBRDADDR, SIOCSIFNETMASK.
46
! GCC % % (glic .), % # ; LSB 3, $ ; ! % (Perl, Python, LAMP .);
% LSB % %
( %, , #! ..).
3.4. Linux Standard Base 4 LSB $ . Z $ #! LSB. 4 % SuSe Linux Enterprise Server, SuSe Linux, RedHat Enterprise Linux, Asianux, Mandrakelinux Corporate Server, SGI ProPack for Linux3. _ , !
Linux . *" #! LSB. Fedora Core. * . , Linux , , IBM #! LSB . 4
! Intel HP. , LSB . , , $ % #$% . Z , LSB , % % Linux % LSB- % !% %
#. ' ,
. LSB - % #! , , ! %
. LSB " , $
% %
# ! . , ! « — » $ $ . Q " ! . & , . ' , % $ , % Linux, " $ ! , % . * % #
% . % $
%% . _ % %
, " , ! . FSG , LSB 4 LSB 3, % , " . , FSG . ? LSB % . ( LSB , % LSB % . #! !
% % , LSB- % . ) , #! LSB . ? IBM Systems Application Advantage for Linux ( "Chiphopper") [Chi06] , 25% , % ,
LSB . ) ! $ . "## % %, $ $ . ' LSB Workgroup % %
$ , !. " +
!
, !
. /, !, $ LSB Workgroup #! LSB.
Red Hat, #! Red Hat Enterprise Linux 4 LSB 3.0 10 . $ #! ,
$ . * "% $ , % UNIX- % % % , ! Linux. = LSB Workgroup #! ,
. LSB Workgroup %$ , $ . LSB Workgroup Z "
: The LSB is an interface standard, and an interface standard is only as good as the tests suites that test those interfaces, so we absolutely have to do better here.
3
#!% Free Standards Group (www.freestandards.org). ) : http://www.freestandards.org/en/Products 47
48
«LSB # ! , # %$, %$ ,
"% # . ) , $ $ .» ? , # %
% % % Linux, #!
. , % ! .
5. )
Linux Standard Base # ! ! Linux? 4 # ? Linux Standard Base "% ! . ? % ? ^ % % ! # ? " . = $ , , LSB Workgroup $
: x
% %% # , % %, $%, % $ ;
[IA32]
Linux Standard Base Core Specifications for IA32 3.1. http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-IA32/LSB-Core-IA32.pdf
[IA64]
Linux Standard Base Core Specifications for IA64 3.1. http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-IA64/LSB-Core-IA64.pdf
[ISOC99]
ISO/IEC 9899-1999. Programming Languages — C. Geneve: ISO, 1999.
[LANANA]
Linux Assigned Names and Numbers Authority. http://www.lanana.org/
[LFS]
Large File Support. http://www.UNIX-systems.org/version2/whatsnew/lfs20mar.html
[LSBC31]
ISO/IEC 23360-1-8:2005, Linux Standard Base (LSB) Core Specification 3.1. Geneve: ISO, 2005.
[LSB98]
Project Proposal and Call for Participation: The Linux Standard Base (LSB) Project. http://old.lwn.net/1998/0528/a/lsb.html
[Mur06]
http://www.freestandards.org/en/LSB_Summit.
[POSIX]
IEEE 1003.1-2004. Information Technology — Portable Operating System Interface (POSIX). New York: IEEE, 2004.
[PPC32]
Linux Standard Base Core Specifications for PPC32 3.1. http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-PPC32/LSB-CorePPC32.pdf
[PPC64]
Linux Standard Base Core Specifications for PPC64 3.1. http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-PPC64/LSB-CorePPC64.pdf
[RPM30]
RPM Package Format V3.0. http://www.rpm.org/
[S390]
Linux Standard Base Core Specifications for S390 3.1. http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-S390/LSB-Core-S390.pdf
x
% ;
x
%, % %
;
[S390X]
x
Linux Standard Base Core Specifications for S390X 3.1. http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-S390X/LSB-CoreS390X.pdf
#! ,
.
[SUSv2]
CAE Specification, January 1997, System Interfaces and Headers (XSH), Issue 5 (ISBN: 1-85912-181-0, C606).
[SUSX]
CAE Specification, May 1996, X/Open Curses, Issue 4, Version 2 (ISBN: 185912-171-3, C610), plus Corrigendum U018.
[SVID3]
American Telephone and Telegraph Company, System V Interface Definition, Issue 3; Morristown, NJ, UNIX Press, 1989.(ISBN 0201566524).
[SVID4]
System V Interface Definition, Fourth Edition.
[AMD64]
Linux Standard Base Core Specifications for AMD64 3.1. http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-AMD64/LSB-CoreAMD64.pdf
[Chi06]
Kay A. Tate, Narender Ramireddy. First Year Chiphopper ISV Experience. IBM Chiphopper Team, June 2006.
[ELF]
Executable and Linking Format. http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-generic/LSB-Coregeneric/elf-generic.html
[FHS]
Filesystem Hierarchy Standard (FHS) 2.3. http://www.pathname.com/fhs/
49
50
если стандарт определяет функциональность, реализуемую фиксируемым им интерфейсом, достаточно точно и недвусмысленно.
Использование формальных методов для обеспечения соблюдения программных стандартов А. И. Гриневич, В. В. Кулямин, Д. А. Марковцев, А. К. Петренко, В. В. Рубанов, А. В. Хорошилов Институт системного программирования РАН (ИСП РАН), Б. Коммунистическая, 25, Москва, Россия E-mail: {tanur,kuliamin,day,petrenko,vrub,khoroshilov}@ispras.ru
Аннотация В статье описывается подход к построению инфраструктуры использования программных стандартов. Предлагаемый подход основан на формализации стандартов и автоматизации построения тестов для проверки соответствия им из полученных формальных спецификаций. В рамках этого подхода предлагается технологическая поддержка для решения ряда возникающих инженерных и организационных задач, что позволяет использовать его для сложных промышленных стандартов программного обеспечения. Этот тезис иллюстрируется использованием описанного метода для формализации ядра Базового стандарта Linux (Linux Standard Base). Данная работа лежит в рамках подходов к решению задачи по обеспечению развития надежных крупномасштабных программных систем, провозглашенной международным академическим сообществом одним из Больших Вызовов в информатике [1,2].
1. Введение Экономическое и социальное развитие общества требует для своей поддержки все более сложное программное обеспечение (ПО). Масштабность современных программных систем приводит к тому, что такие системы строятся из многих компонентов, разрабатываемых в разных организациях огромным количеством людей. Для построения работоспособных систем на основе такого подхода необходимо уметь решать задачи обеспечения совместимости между различными их компонентами и их надежности в целом. Одно из наиболее эффективно работающих на практике решений этой задачи — выработка и соблюдение стандартов на программные интерфейсы между отдельными компонентами. Идея, лежащая в основе использования интерфейсных стандартов, проста — настаивая на их соблюдении, мы обеспечиваем компонентам, созданным разными разработчиками, возможность взаимодействовать через стандартизованные интерфейсы. Таким образом, их совместимость обеспечивается без введения чересчур жестких ограничений на возможные реализации, что ограничило бы как творчество отдельных разработчиков, так и инновационный потенциал компанийпоставщиков ПО. Этот подход хорошо работает,
51
Однако, тексты многих современных стандартов, описывающих требования к программным интерфейсам, далеко не так точны. Это объясняется историей их появления. Обычно такие стандарты разрабатываются под давлением требований рынка и должны принимать во внимание противоречивые интересы многих поставщиков программного обеспечения, интерфейс которого предполагается стандартизовать. В таких условиях только базовая функциональность может быть определена непротиворечивым образом, а для сложных и специфических функций каждая группа разработчиков предлагает свое собственное решение. Поскольку многие поставщики ПО уже вложили деньги в имеющиеся у них решения, им очень тяжело отказаться от этих инвестиций в пользу варианта одного из конкурентов, который при этом остается в выигрыше. Чаще всего группа по выработке стандарта приходит к компромиссу, при котором основные поставщики должны внести примерно одинаковые по объему изменения в ПО каждого из них, а в тех случаях, где необходима серьезная переработка, стандарт осознанно делается двусмысленным, чтобы любое из уже имеющихся решений могло декларировать соответствие ему. Это позволяет поставщикам ПО потратить приемлемые для них деньги на обеспечение соответствия своих систем стандарту, но в то же время подрывает столь желаемую совместимость различных его реализаций. Некоторые из действующих стандартов на интерфейсы ПО или на телекоммуникационные протоколы появились еще 20-25 лет назад и неоднократно пересматривались. Конечно же, каждая новая их версия становилась более строгой и непротиворечивой, и большинство проблем первых выпусков уже устранено. Однако неясности и противоречия постоянно вносятся в новых добавлениях, которые так и будут появляться в связи с постоянным развитием технологий. Выход из этого замкнутого круга многие специалисты по программной инженерии видят в формализации требований стандартов. Сама попытка переформулировать их в строгом виде вскрывает противоречия и двусмысленности. Формализация большого текста, конечно, требует приложения колоссальных усилий, но она может быть проведена не в один прием, а постепенно, инкрементально. Никто также не ожидает, что все неточности удастся удалить из стандарта одним махом. Начав процесс формализации, мы, по крайней мере, получаем информацию об имеющихся реальных проблемах стандарта и можем предлагать и анализировать различные варианты их решения. Формализация делает стандарт более точным, а значит, и более полезным, но она одна все же недостаточна для адекватного обеспечения соблюдения стандарта на практике. Разработчики ПО нуждаются во вспомогательных инструментах, которые позволят им убедиться в соответствии или несоответствии той или иной системы стандарту. Поэтому, практически полезное формальное
52
описание требований стандарта должно дополняться набором тестов, которые проверяют соответствие этим требованиям. Эта статья представляет подход к формализации стандартов и разработке тестовых наборов, основанный на одной из наиболее детально проработанных методик тестирования программного обеспечения на соответствие стандартам, созданной для использования в разработке телекоммуникационного ПО и зафиксированной в стандартах ISO 9646 [3] и ITU-T Z.500 [4]. Продуманная методика делает тестирование на соответствие стандарту более строгим и тем самым обеспечивает более высокое качество ПО, реализующего его. Однако практическое использование наборов тестов на соответствие порождает ряд инженерных и организационных вопросов, без решения которых невозможно перенести применение этой методики для небольших примеров на современные стандарты, описывающие очень сложные системы. Можно отметить следующие аспекты.
Прослеживаемость требований. Для разработчиков ПО и их руководителей подлинным источником требований является сам текст стандарта. Формальные спецификации же — это какой-то дополнительный документ, который должен явно продемонстрировать свою связь со стандартом. То же самое относится и к тестам, полученным из формальных спецификаций — они должны соотноситься с требованиями, описанными в каких-то разделах стандарта. Прослеживаемость требований стандарта в тестах является практической потребностью всякой осмысленной деятельности по их реализации.
Покомпонентное рассмотрение стандарта. Промышленные стандарты иногда очень сложны и часто имеют большой объем, так же как и промышленные программные системы. Для того чтобы адекватно анализировать их, необходимы какие-то техники декомпозиции. Формализм, используемый для спецификаций, должен позволять выделять в стандарте компоненты разных уровней, и анализировать их по отдельности. Должна поддерживаться также и последующая интеграция таких компонентов в единое целое.
Управление изменениями. Стандарты, как и их реализации, не пребывают в неизменном виде — они постоянно изменяются под давлением общего технологического прогресса. Необходима адекватная поддержка изменений в используемом формализме, методах построения тестов и инструментах. Ее отсутствие быстро сделает полученные спецификации и тесты практически бесполезными.
Управление конфигурациями. Очень часто стандарты, используемые на практике, описывают системы с большим числом параметров– конфигурационных опций, значительно влияющих на поддерживаемую функциональность. Возможность выбора конфигурации и проверки только относящихся к ней требований также должна быть заложена в спецификации, тесты и методику их разработки.
53
Все перечисленные проблемы должны иметь удобные решения в рамках технологии построения и эксплуатации инфраструктуры для обеспечения соблюдения стандартов. В данной статье рассказывается о подходе, претендующем на роль такой технологии и основанном на методе автоматизированной разработки тестов UniTesK, созданном в ИСП РАН. Основные идеи и техники этого подхода рассматриваются в следующих двух разделах статьи. Четвертый раздел представляет предварительные результаты его применения к Базовому стандарту Linux (Linux Standard Base) [5], промышленному стандарту на интерфейс базовых библиотек операционной системы Linux. В последних двух разделах статьи содержится краткий обзор других подходов к построению тестов на соответствие стандартам и заключение, формулирующее основные результаты и перечисляющее направления возможного развития предложенного подхода.
2. Формализация стандартов Основные трудности, возникающие при формализации индустриальных стандартов, связаны с их неформальной природой. Главные цели такого стандарта — зафиксировать консенсус ведущих производителей относительно функциональности рассматриваемых систем и обеспечить разработчиков реализаций стандарта и приложений, работающих на основе этих реализаций, справочной информацией и руководством по использованию описанных функций. Форма и язык стандарта выбираются так, чтобы достигать этих целей. Стандарты на программные интерфейсы чаще всего состоят из двух частей: обоснования (rationale), представляющего целостную картину основных концепций и функций в рамках данного стандарта, и справочника (reference), описывающего каждый элемент интерфейса отдельно. В дополнение к элементам интерфейса (типам данных, функциям, константам и глобальным переменным) справочник может описывать и более крупные компоненты: подсистемы, заголовочные файлы и пр. Отдельные разделы справочника могут ссылаться друг на друга и содержать части друг друга. Формализация стандарта включает несколько видов деятельности. 1. Декомпозиция стандарта. Так как количество интерфейсных элементов, описанных в стандарте, может быть очень велико, то на первом шаге они разбиваются на логически связанные группы. Такая группа обычно состоит из операций и типов данных, связанных с определенным набором близких функций, и замкнута относительно обращения операций. Например, операции открытия и закрытия файлов, создания и удаления объектов нужно помещать в одну группу. Вся дальнейшая работа производится в рамках таких групп. 2. Выделение требований. Следующая задача заключается в выделении всех требований стандарта к описываемым им элементам интерфейса, которые могут быть проверены. Так как одна и та же вещь может быть описана в нескольких местах, то все соответствующие разделы стандарта необходимо внимательно, фраза за фразой, прочитать и пометить все найденные
54
требования и ограничения. Затем выделенные требования объединяются в непротиворечивый набор. Эта работа достаточно утомительная и тяжелая, но ни в коем случае не механическая. Ее сложность связана с преобразованием неформальных требований и ограничений в формальные, а, по словам М. Р. Шуры-Буры, переход от неформального к формальному существенно неформален.
соответствующему месту в тексте стандарта. Одно требование обычно соответствует логически замкнутому фрагменту текста, выражающему одно ограничение на элемент интерфейса или элемент данных. Каждое требование имеет уникальный идентификатор. Каталог требований позволяет в дальнейшем оценивать адекватность тестирования в терминах исходного текста стандарта.
Утверждения и ограничения из разных частей стандарта могут быть несовместимыми, двусмысленными, представлять похожие, но не одинаковые идеи. Только текста стандарта часто не достаточно, чтобы прийти к непротиворечивой полной картине. Поэтому при выделении требований необходимо пользоваться общими знаниями о предметной области, книгами и статьями по тематике, знанием о используемых в существующих системах решениях, а также общаться с авторами стандарта, экспертами в области и опытными разработчиками. Некоторые аспекты специально не определяются стандартом точно, для того чтобы реализации различных производителей соответствовали ему. Занятный пример можно найти в текущей версии стандарта POSIX [6]. Описание функций fstatvfs() и statvfs() из sys/statvfs.h говорит: «Не специфицировано, имеют ли все члены структуры statvfs смысл для всех файловых систем» ("It is unspecified whether all members of the statvfs structure have meaningful values on all file systems"). Можно ничего не проверять. В этом случае никаких требований из соответствующей части стандарта не извлекается.
Если существует небольшое количество возможных реализаций, то требования к ним могут быть представлены в виде параметризированных ограничений. При этом вводится конфигурационный параметр, разные значения которого соответствуют различным возможным реализациям и определяют конечный вид проверяемого требования.
Размеченный текст стандарта. Это исходный текст стандарта, в котором наглядным образом, например цветом, выделены места, соответствующие требованиям из каталога. Размеченный текст позволяет проверить полноту выполненного анализа текста стандарта и убедиться в том, что ни одно требование не пропущено.
Дефекты стандарта и замечания к его тексту. Это список обнаруженных двусмысленностей, несоответствий между утверждениями в различных частях стандарта, ненамеренно неясных и неточных ограничений, неполных описаний функциональности и т.д. Этот список передается группе разработчиков стандарта, и позволяет сделать следующие его версии более точными и непротиворечивыми.
3. Разработка спецификаций. Эта работа обычно выполняется параллельно и вперемешку с предыдущей. Они разделены только в целях ясности изложения. Большинство найденных требований записываются в форме контрактных спецификаций операций, типов данных и элементов данных. Каждая операция описывается с помощью предусловия и постусловия. Предусловие операции определяет область ее определения. Постусловие определяет ограничения на результаты работы операции и итоговое состояние системы в зависимости от значений ее входных параметров и состояния системы до ее вызова. Для типов данных и элементов данных определяются ограничения целостности, называемые инвариантами.
Существует два возможных пути разрешения подобных ситуаций.
Те требования, которые не оформляются в виде контрактных спецификаций, делятся на следующие группы.
Пример из описания функции basename() в стандарте POSIX: «Если строка, на которую указывает параметр path, равна “//”, то реализация может возвращать как “/”, так и “//”» ("If the string pointed to by path is exactly “//”, it is implementation-defined whether “/” or “//” is returned'').
Непроверяемые требования. Часть требований не проверяется, потому что их вообще нельзя проверить с помощью конечной процедуры тестирования, или потому что не существует методов надежной проверки таких требований (например, «функция должна выдавать результат за время, меньшее, чем любой многочлен второй степени от значения ее параметра»), а также из-за крайней неэффективности их проверки («возвращаемый функцией указатель должен отличаться от указателя на любой объект, присутствовавший в системе до ее вызова»).
Проверяемые требования. Другая часть требований проверяется не в спецификациях по соображениям эффективности тестов и удобства их модификации. Ряд требований, касающихся целостности данных
Выделение требований проводится до тех пор, пока весь текст стандарта, касающийся выбранной группы элементов интерфейса, не будет разбит на требования, которые можно проверить, и остальные фразы, не содержащие проверяемых утверждений. Основные результаты этой работы следующие.
Каталог требований. Он состоит из списка проверяемых требований, налагаемых стандартом, и привязывает каждое требование к
55
56
некоторых типов, может быть отражен в самом описании их структуры в спецификациях. При этом проверка соответствующих ограничений происходит при преобразовании из данных реализации в модельные данные. Если эти ограничения нарушаются, такое преобразование становится невозможным и спецификация не может с ними работать, но сообщение об их проверке и обнаруженном несоответствии появится в трассе теста. Некоторые требования можно проконтролировать только в результате многократных обращений к тестируемой функции и проверки каких-то свойств всей совокупности полученных при этом результатов. Эти требования часто неудобно оформлять в виде спецификаций — для них создается отдельный тестовый сценарий (см. ниже), который производит все нужные обращения и проверяет необходимые свойства полученных результатов. Примером такой ситуации может служить требование к функции rand() генерировать при многократных обращениях последовательность случайных чисел, равномерно распределенных на некотором интервале. Для проверки этого свойства можно накопить данные о результатах ее вызовов и применить к их совокупности, например, критерий Колмогорова.
4. Определение критериев покрытия. Последний вид деятельности в рамках формализации стандарта заключается в определении критериев покрытия, которые могут быть использованы для измерения адекватности тестирования реализации на соответствие стандарту. Базовый критерий — это необходимость покрытия всех требований стандарта, применимых к текущей конфигурации тестируемой системы. Более строгие критерии могут определять дополнительные ситуации для тестирования. Все эти критерии базируются на спецификациях, представляющих требования стандарта. Критерии покрытия сложным образом связаны с конфигурационными параметрами. Возможность покрытия определенной ситуации может зависеть от текущих значений конфигурационных параметров, и эта зависимость должна быть зафиксирована, чтобы избежать многочисленных трудностей при анализе результатов тестирования. Результатом этой работы является набор критериев покрытия, тесно связанный с конфигурационной системой. Процесс формализации стандарта и последующей разработки тестов на основе ее результатов проиллюстрирован на Рис. 1.
Разработка спецификаций имеет два результата.
Формальные спецификации всех элементов интерфейса. Код спецификаций размечается, чтобы указать связь между описанными формальными ограничениями и соответствующими им требованиями из каталога требований.
Конфигурационная система стандарта. Эта система состоит из набора конфигурационных параметров, как объявленных в стандарте, так и дополнительно введенных разработчиками спецификаций, зависимостей между ними, а также связей между ними, элементами интерфейса и проверяемыми ограничениями. Некоторые конфигурационные опции, представленные как значения параметров, управляют набором проверяемых требований. Другие могут говорить о том, что определенная функциональность вообще отсутствует в системе, и соответствующие операции не должны вызываться. Третьи могут влиять на возможные коды ошибок, возвращаемые операциями. Пример
Общие знания (литература, существующие системы, пр.)
Замечания к стандарту
Каталог требований
Общение (с экспертами, разработчиками стандарта)
Формализация
Размеченный текст стандарта Спецификации
Критерии покрытия
Конфигурационная система стандарта
Разработка тестов Тестовый набор
первого
случая можно увидеть в описании функции pthread_create() из стандарта POSIX: «Если макрос _POSIX_THREAD_CPUTIME определен, то новый поток должен иметь доступ к часам процессорного времени, и начальное значение этих часов должно быть выставлено в ноль» ("If _POSIX_THREAD_CPUTIME is defined, the new thread shall have a CPU-time clock accessible, and the initial value of this clock shall be set to zero'').
57
Стандарт
Конфигурационная система теста
Рисунок 1. Входные и выходные данные формализации стандарта и разработки тестов.
58
3. Разработка тестов на соответствие стандарту
Реальный мир
В основе процесса разработки тестов на соответствие стандарту лежит технология UniTesK, разработанная в ИСП РАН на базе многолетнего опыта проектов по тестированию сложного промышленного ПО. Эта технология использует подход к формальному тестированию, разработанный в работах Берно (Bernot) [7], Бринксмы (Brinksma) и Тритманса (Tretmans) [8,9] и описанный в телекоммуникационных стандартах ISO 9646 [3] и ITU-T Z.500 [4]. Основные положения этого подхода можно сформулировать следующим образом (Рис. 2 иллюстрирует связи между упоминаемыми ниже понятиями).
Стандарт
Требования стандарта представлены в виде модели, описанной с помощью некоторого формализма. Эта модель называется спецификацией.
Предполагается, что программная система, чьё соответствие стандарту мы пытаемся установить — тестируемая система, — может быть адекватно смоделирована с помощью этого же формализма. Соответствующую нашей системе модель мы называем реализацией. Мы не знаем её деталей, но предполагаем, что она существует. Адекватность моделирования в данном случае означает, что мы не можем наблюдать никаких различий между реальным поведением тестируемой системы и модельным поведением реализации.
Тот факт, что тестовая система соответствует стандарту, моделируется посредством отношения соответствия (conformance relation, implementation relation) между реализацией и спецификацией.
Модельные тесты строятся на основе спецификации или извлекаются из нее. Модельный тест — это модель в том же самом формализме, способная взаимодействовать с другими моделями и выдающая булевский вердикт как результат этого взаимодействия. Реализация проходит модельный тест, если он выдаёт вердикт «истина» в результате взаимодействия с ней. Имеет смысл строить только значимые тесты, т.е. те, что проходятся любой реализацией, соответствующей спецификации. Тест, не являющийся значимым, может отвергнуть совершенно корректную реализацию. Результатом извлечения тестов является набор модельных тестов или модельный тестовый набор. Желательно, чтобы он был полным тестовым набором — таким, что реализация проходит все тесты из него тогда и только тогда, когда она соответствует спецификации.
Модельные тесты транслируются в тестовые программы, взаимодействующие с тестируемой системой. Поскольку мы считаем, что это взаимодействие моделируется взаимодействием между модельным тестом и реализацией, можно сделать вывод, что тестируемая система, проходящая полный тестовый набор, соответствует стандарту.
59
соответствует Тестируемая система
Модельный мир моделирование
Спецификация
соответствует Реализация
проходит Тестовый набор
извлечение тестов
проходит
трансляция
Модельный тестовый набор
Рисунок 2. Отношения между реальными сущностями и их моделями.
Используемые на практике стандарты обычно описывают довольно сложные и большие системы. Спецификации таких стандартов также сложны и объёмны, так что любой полный тестовый набор для такой спецификации содержит бесконечное множество тестов. Для того чтобы сделать формальное тестирование осуществимым на практике, мы добавили в описанный базовый подход понятие критерия тестового покрытия и нацеленность набора тестов на достижение критерия.
Критерий тестового покрытия — это некоторое отношение эквивалентности на модельных тестах. Фактически, выбор такого критерия означает, что все равно, какой из эквивалентных тестов выполнять — все они должны давать одинаковые результаты. Поэтому, используя критерий тестового покрытия, мы выдвигаем гипотезу, что реализация обязана либо проходить оба эквивалентных теста, либо не проходить ни один из них. Обычно критерий стараются выбрать так, чтобы большинство разумных реализаций обладало этим свойством. Тестовый набор называется полным относительно критерия покрытия, если объединение классов эквивалентности по этому критерию тестов из этого набора есть полный тестовый набор в определенном выше смысле.
Следует обратить внимание, что критерий покрытия может быть выбран исходя из различных соображений. Единственное обязательное свойство —
60
наличие конечного тестового набора, полного относительно избранного критерия. При построении тестового набора для проверки на соответствие стандарту разумно использовать критерии, построенные на основе текста стандарта и структуры спецификаций, являющихся его формальным представлением. Технология UniTesK детально рассматривалась в работах [10-13]. Здесь мы приводим только краткое описание ее основных идей.
тестируемой системы Функциональные требования к поведению представляются в виде контрактных спецификаций. Контракт для отдельного компонента состоит из пред- и постусловий для всех его операций и асинхронных событий, которые он может создавать, и инвариантов, определяющих ограничения целостности его внутренних данных.
Критерии тестового покрытия определяются на основе структуры постусловий операций отдельного компонента или нескольких компонентов. Примерами таких критериев являются покрытие ветвей в коде постусловия и покрытие дизъюнктов из дизъюнктивной нормальной формы условий этих же ветвлений. Есть возможность вводить произвольные критерии, описывая дополнительные ситуации, в которых работу системы необходимо проверить.
Тестовый сценарий создается для достижения определённого покрытия заданной группы операций. Такой сценарий определяет конечный автомат, моделирующий поведение тестируемого компонента таким образом, что выполнение всех его переходов гарантирует покрытие 100% ситуаций в соответствии с выбранным критерием. Автомат задается при помощи функции вычисления состояния и набора допустимых действий в произвольном состоянии. Этот набор действий зависит от данных состояния. Каждое действие соответствует вызову одной из операций с некоторыми аргументами.
4. Применения описанного подхода Представленный выше подход был успешно использован для формализации и создания наборов тестов для частей стандартов на протоколы IPv6 и IPMP2 [11,14]. Более масштабным применением этого подхода стал проект Центра верификации ОС Linux [15] по формализации Базового стандарта Linux (Linux Standard Base, LSB) [5] и разработке тестового набора для проверки соответствия ему. Проект был назван OLVER — Open Linux Verification. Цель первой фазы проекта — создать формальные спецификации и соответствующий тестовый набор для 1532 функций основных библиотек Linux, перечисленных в разделах III и IV стандарта LSB 3.1 (они описывают базовые библиотеки и библиотеки утилит). Стандарт LSB задает требования ко многим из этих функций, ссылаясь на уже существующие стандарты. Непосредственно в тексте LSB описывается лишь 15% интерфейсов, большинство — 60% функций — определяются в стандарте Single UNIX Specification [6], который включает текущую версию POSIX, а остальные — в таких спецификациях как X/Open Curses [16], System V Interface Definition [17] и ISO/IEC 9899 (стандарт языка C) [18]. LSB не включает в себя все эти стандарты, а ссылается лишь на их части, касающиеся описания требований для отдельных функций. Всего, вместе со стандартами, на которые он ссылается, LSB состоит из более чем 6000 страниц текста. Первая фаза проекта завершится в конце 2006 года. Все ее результаты будут доступны в виде открытого кода на сайте проекта [15]. В составе этих результатов будет следующее.
Дополнения к стандарту LSB Core 3.1 в виде формальных спецификаций его требований на расширении языка С (SeC — Specification Extension of C). Такие спецификации выражают требования стандарта в формальном виде, позволяя прояснить нечеткие места в его тексте и сделать явными все подразумеваемые в нем ограничения. Они послужат основой для разработки тестового набора для проверки соответствия стандарту LSB.
Список замечаний к текстам LSB и связанных стандартов, наработанных в ходе формализации. Это указания на нечеткие, двусмысленные, противоречивые или ошибочные места в стандартах. Они будут направляться разработчикам стандартов для внесения изменений в их будущие версии.
Тестовый набор для проверки соответствия поведения системных интерфейсов конкретной реализации Linux требованиям стандарта LSB Core 3.1. Он будет представлять собой набор программ, которые в результате своего выполнения строят HTML-отчеты о проверенных требованиях и найденных несоответствиях. Тестовый набор будет иметь систему конфигурации, которая позволит настраивать тесты на особенности конкретной тестируемой системы, допускаемые стандартом, а также установить параметры, определяющие состав тестируемых модулей и глубину тестирования.
Сами тесты или последовательности тестовых воздействий генерируются при выполнении тестового сценария, во время которого автоматически строится некоторый путь, покрывающий все переходы описываемого им автомата.
Построение тестового набора для проверки соответствия практически используемым стандартам ПО приводит к необходимости разработки конфигурационной системы тестов. Эта система включает конфигурационную систему стандарта (см. выше) и дополнительные параметры, управляющие работой тестов. Разные значения этих параметров соответствуют различным критериям покрытия, разным наборам тестовых сценариев и т.п. Хорошая конфигурационная система делает тестовый набор применимым в любых условиях, где может функционировать реализация рассматриваемого стандарта.
61
62
В начале проекта 1532 функции LSB были разбиты на 170 групп функционально связанных операций, которые в свою очередь группируются в большие подсистемы в соответствии с общей функциональностью — потоки, межпроцессное взаимодействие, файловая система, управление динамической памятью, математические функции, и т.д. За первые 3 месяца работы были проспецифицированы и снабжены базовыми тестовыми сценариями 320 функций из 41 группы. В процессе формализации было выделено около 2150 отдельных требований стандарта к этим функций. При этом к некоторым функциям предъявляется всего лишь несколько требований, в то время как к другим — несколько десятков. На 1 июня 2006 года проанализирован текст стандарта, касающийся 930 функций. Выделено 10500 элементарных требований. Из этих функций для 740 разработаны формальные спецификации и тесты. Полученный на это момент набор спецификаций и тестов содержит около 400 тысяч строк кода. Текущее состояние проекта отражается на сайте [15]. Качество разрабатываемых тестов определяется исходя из покрытия требований, но сравнить по этой характеристики несколько различных тестовых наборов для тестирования функциональности достаточно тяжело, в них почти всегда элементарные требований выделяются по-разному (см. далее). Если же сравнивать покрытие кода для некоторых групп функций, можно отметить, что полученный в этом проекте тестовый набор дает большее покрытие, чем другие известные наборы для тестирования функциональности. Получаемое покрытие кода приближается к результатам отладочного тестового набора для библиотеки glibc [19], созданного ее разработчиками, обладающими детальными знаниями об особенностях реализации различных ее функций, и не нацеленного на проверку соответствия каким-либо стандартам. Результаты сравнения покрытия кода для некоторых групп функций для тестовых наборов LTP [20], официального тестового набора LSB [21], отладочного тестового набора glibc [19] и набора, полученного в рассматриваемом проекте, приведены в Таблице 1. Группа функций
LTP [20]
LSB TS [19]
glibc TS [21]
OLVER [15]
Управление потоками
48%
71%
78%
72%
Преобразования строк
53%
67%
84%
91%
Функции поиска данных
26%
33%
70%
65%
Таблица 1. Сравнение получаемого покрытия кода для некоторых групп функций.
На основе достигнутой производительности можно утверждать, что первая фаза проекта потребует около 15 человеко-лет. Чтобы начать работать в этом проекте, опытному разработчику программного обеспечения (не обычному тестировщику!) требуется около месяца для обучения особенностям технологии и изучения
сопутствующих процессов. Полученные предварительные результаты позволяют надеяться, что рассмотренный подход может успешно применяться для проектов такого масштаба.
5. Другие подходы к построению тестов на соответствие стандартам Наиболее глубокие результаты в области формализации стандартов и методик разработки тестов на основе формальных спецификаций получены применительно к телекоммуникационным протоколам. Общий подход к формальному тестированию, представленный в работах [4,7,9] (см. также выше), тоже был разработан в этой области. Этот подход имеет много общего с представленным в данной статье. Различия связаны, в основном, с необходимостью иметь дело со стандартами большего объема, такими как стандарты POSIX или LSB на интерфейсы библиотек операционных систем. Большой размер стандартов и соответствующих спецификаций приводит к практической невозможности использовать как полные тестовые наборы в терминах работы [9], поскольку они бесконечны, так и выбор набора целей тестирования «на глаз», который является одним из традиционных шагов разработки тестов для телекоммуникационных протоколов. Вместо этого используется более систематическое выделение отдельных требований, понятие критерия тестового покрытия, выбор критерия, ориентированного на учет всех выявленных требований, и генерация тестов, нацеленная на достижение высоких показателей покрытия по выбранному критерию. Использование контрактных спецификаций также способствует большей практичности и масштабируемости нашего подхода. Программные контракты, с одной стороны, позволяют провести декомпозицию большой системы на более обозримые компоненты, что труднее сделать, используя автоматы или системы переходов, лежащие в основе традиционного формального тестирования. С другой стороны, пред- и постусловия лучше подходят для описания недетерминированного поведения, которое довольно часто вынужден фиксировать стандарт при наличии одну и ту же абстрактную нескольких возможностей реализовать функциональность. Статья [22] представляет другую попытку формализации стандарта на примере IEEE 1003.5 — POSIX Ada Language Interfaces (интерфейсы POSIX для языка Ada). В рамках описываемого в ней метода на базе требований стандарта сразу строились формальные описания проверяющих их тестов, без промежуточных спецификаций самих требований. Такой метод кажется нам принципиально мало отличающимся от традиционной ручной разработки тестов, при которой разработчик теста читает стандарт, придумывает на основе прочитанного тесты и записывает их в виде некоторых программ. Более близкий к рассматриваемому в данной статье подход используется в работе [23], где представлены результаты практического использования инструмента
63
64
автоматизированного создания тестов GOTCHA-TCBeans. Этот инструмент использовался, в частности, для построения тестов для функции fcntl() стандарта POSIX. По ряду идей, касающихся использования формальных техник для тестирования, представленный в [23] подход очень похож на используемый нами, хотя он более сфокусирован на технике создания формальных моделей, чем на обеспечении их адекватности и поддержке прослеживаемости связей между формальными спецификациями и исходным текстом стандарта. Кроме того, используемые в [23] формальные спецификация являются описаниями конечных автоматов на специализированном языке Mur, а не программными контрактами, как в нашем подходе. Существует ряд аналогичных работ по разработке тестовых наборов для проверки соответствия стандартам интерфейсов операционных систем. Наиболее известные стандарты в этой области это IEEE Std 1003.1, или POSIX [6], и Базовый стандарт Linux, или LSB [5]. Имеются и наборы тестов на соответствие этим стандартам — это сертификационные тесты POSIX от Open Group [24], открытый проект Open POSIX Test Suite [25], и официальные сертификационные тесты на соответствие LSB [21] от Free Standards Group. Все эти проекты используют схожие технологии выделения требований из текста стандарта и создания соответствующего каталога требований. После этого тесты разрабатываются вручную на языке C с применением подхода «один тест на одно требование». Они не используют формализацию требований и автоматическую генерацию тестов. Стоит отметить, что использование подхода «один тест на одно требование» создает предпосылки для укрупнения требований при их выделении, так как велик соблазн проверить как можно больше в одном тесте. К примеру, в тестах Open POSIX мы обнаружили требования, которые включают в себя десяток утверждений, которые в проекте Центра верификации ОС Linux выделяются в отдельные требования. Такое укрупнение требований может приводить к тому, что некоторые утверждения стандарта упускаются из виду и не проверяются, в то время как крупное интегральное требование, в которое они входят, считается протестированным. В результате построенный на основе таких требований отчет об их покрытии может искажать реальное положение дел, утверждая, что все требования стандарта проверены. Кроме того, автоматическая генерация тестов из спецификаций, используемая в нашем подходе, делает тестовый набор более управляемым, позволяя описывать сами требования стандарта в одном месте, а технику, используемую для их проверки, и тестовые данные — в другом. Это значительно облегчает внесение изменений в тесты при развитии стандарта или их адаптации под требования специфической предметной области.
6. Заключение Внедрение и обеспечение соблюдения стандартов на программные интерфейсы связаны с большим количеством сложных проблем, как технического
65
характера, так и экономических и социальных. Тем не менее, все эксперты сходятся на том, что эта деятельность необходима для стабильного развития производства программного обеспечения. Формализация стандартов уже неоднократно предлагалась в качестве возможного решения множества технических задач, связанных с ней. Однако формализация требует огромных усилий, что позволяет усомниться в применимости подходов на ее основе к реально используемым программным стандартам, достаточно объемным и сложным. Устранить эти сомнения помогут только практические примеры применения подобных методов к таким стандартам. Упомянутый выше проект [15] по формализации LSB похоже, является одной из первых попыток формализации значительной части используемого на практике стандарта высокой сложности и позволяет почувствовать все ее выгоды и недостатки. Мы считаем, что представленный в этой статье подход позволит успешно справляться с подобными задачами. Аргументами в пользу этого мнения являются стабильное продвижение описанного проекта, история применений технологических составляющих данного подхода на практике ([12,14]) и лежащие в его основе базовые принципы хорошего проектирования больших систем [12,13]. Тот факт, что многие инженерные и организационные вопросы также находят отражение в этом подходе, позволяет надеяться, что, избежав участи многих проектов по использованию передовых методик разработки ПО на практике, мы сможем получить действительно полезные практические результаты,. Описанный проект является частью проводимых международным сообществом исследований подходов к решению проблемы обеспечения развития надежных крупномасштабных программных систем, одного из Больших Вызовов в информатике [1,2]. Тони Хоар (Tony Hoar) предложил вести работы по этой проблеме в двух направлениях: разрабатывать методы и инструменты, в перспективе способные помочь в ее решении, и накапливать примеры использования подобных методов на программных системах реалистичных размеров и сложности (“challenge codes”). Второе направление необходимо как для проверки работоспособности подходов, предлагаемых в качестве возможных решений проблемы, так и для демонстрации путей от исследовательских работ в программной инженерии к их практическим приложениям, для нащупывания области применимости разрабатываемых в академической среде передовых подходов и демонстрации их возможностей инженерам-практикам и представителям промышленности. Для стимулирования деятельности в этом направлении и вовлечения в него широких кругов разработчиков ПО ИСП РАН передает результаты этого проекта и все инструменты, необходимые для работы с ними, сообществу разработчиков ПО с открытым кодом (open source community). Мы надеемся также на помощь членов этого сообщества в решении более масштабных задач, таких, как формализация большого числа стандартов, входящих в набор Carrier Grade Linux [26], стандартов, регламентирующих встроенные версии Linux и Linux для систем
66
реального времени, программирования.
а
также
стандартов
на
широко
используемые
языки [16] http://www.opengroup.org/bookstore/catalog/c610.htm
Благодарности. Мы благодарим Федеральное агентство по науке и инновациям Российской Федерации за предоставленную финансовую поддержку для создания Центра верификации ОС Linux и проведения проекта по формализации LSB.
[17] http://www.caldera.com/developers/devspecs/ [18] ISO/IEC 9899. Programming Languages — C. ISO, Geneve, 1999. [19] ftp://ftp.gnu.org/gnu/glibc [20] http://ltp.sourceforge.net
Литература
[21] http://www.linuxbase.org/download/#test_suites
[1]
http://www.fmnet.info/gc6/
[2]
Tony Hoare and Robin Milner, eds. Grand Challenges in Computing. Research. http://www.ukcrc.org.uk/gcresearch.pdf
[22] J. F. Leathrum and K. A. Liburdy. A Formal Approach to Requirements Based Testing in Open Systems Standards. In Proc. of 2-d International Conference on Requirements Engineering, 1996, pp. 94–100.
[3]
ISO 9646. Information Theory — Open System Interconnection — Conformance Testing Methodology and Framework. ISO, Geneve, 1991.
[23] E. Farchi, A. Hartman, and S. S. Pinter. Using a model-based test generator to test for standard conformance. IBM Systems Journal, 41:89–110, 2002.
[4]
ITU-T. Recommendation Z.500. Framework on formal methods in conformance testing. International Telecommunications Union, Geneve, Switzerland, 1997.
[24] http://www.opengroup.org/testing/testsuites/TestSuiteIndex.html [25] http://posixtest.sourceforge.net/
[5]
http://www.linuxbase.org/spec
[26] http://www.osdl.org/lab_activities/carrier_grade_linux
[6]
http://www.unix.org/version3/ieee_std.html
[7]
G. Bernot. Testing against Formal Specifications: A Theoretical View. In Proc. of TAPSOFT'91, Vol. 2. S. Abramsky and T. S. E. Maibaum, eds. LNCS 494, pp. 99–119, Springer-Verlag, 1991.
[8]
E. Brinksma, R. Alderden, R. Langerak, J. van de Lagemaat, and J. Tretmans. A formal approach to conformance testing. In J. de Meer, L. Mackert, and W. Effelsberg, eds. 2nd Int. Workshop on Protocol Test Systems, pp. 349–363. North-Holland, 1990.
[9]
J. Tretmans. A Formal Approach to Conformance Testing. PhD thesis, University of Twente, Enschede, The Netherlands, 1992.
[10] I. Bourdonov, A. Kossatchev, V. Kuliamin, and A. Petrenko. UniTesK Test Suite Architecture. In Proc. of FME 2002. LNCS 2391, pp. 77–88, Springer-Verlag, 2002. [11] V. Kuliamin, A. Petrenko, N. Pakoulin, A. Kossatchev, and I. Bourdonov. Integration of Functional and Timed Testing of Real-time and Concurrent Systems. In Proc. of PSI 2003, LNCS 2890, pp. 450–461, Springer-Verlag, 2003. [12] В. В. Кулямин, А. К. Петренко, А. С. Косачев, И. Б. Бурдонов. Подход UniTesK к разработке тестов. Программирование, 29(6):25–43, 2003. [13] V. Kuliamin. Model Based Testing of Large-scale Software: How Can Simple Models Help to Test Complex System. In Proc. of 1-st International Symposium on Leveraging Applications of Formal Methods, Cyprus, October 2004, pp. 311–316. [14] V. Kuliamin, A. Petrenko, and N. Pakoulin. Practical Approach to Specification and Conformance Testing of Distributed Network Applications. In M. Malek, E. Nett, N. Suri, eds. Service Availability. LNCS 3694, pp. 68–83, Springer-Verlag, 2005. [15] http://www.linuxtesting.ru
67
68
Правильность их проверяется как соответствие этих результатов ожиданиям пользователей в соответствующей ситуации.
Формальные подходы к тестированию математических функций
В то же время, область использования сложного ПО в человеческой деятельности все расширяется, и во многих случаях решаемые им задачи таковы, что даже по косвенным признакам уже нелегко судить о правильности полученных с его помощью результатов. Можно привести следующие примеры таких задач.
В. В. Кулямин Институт системного программирования РАН (ИСП РАН), Б. Коммунистическая, 25, Москва, Россия E-mail:
[email protected]
Аннотация Данная статья рассматривает вопросы проверки корректности вычисления математических функций на числах с плавающей точкой, формат которых определяется стандартом IEEE 754. В ней описывается метод разработки тестов для реализаций таких функций, основанный на формальных спецификациях их поведения. Предлагаемый метод основан на технологии разработки тестов UniTESK и двух дополнительных методиках: методике формирования строгих требований к реализации математической функции и методике построения набора тестовых данных для ее тестирования. Описанные методики опираются на специфические свойства представления чисел с плавающей точкой и особенности поведения самой тестируемой функции.
Моделирование космогонических процессов: развития звезд, планетных систем и галактик. Моделирование космических катастроф, последствия которых могут угрожать существованию человечества.
Моделирование физических процессов в экстремальных условиях: движения со сверхвысокими скоростями в вязких средах, поведения элементарных частиц, поведения плазмы в термоядерном реакторе, поведения материалов в условиях сверхнизких или сверхвысоких температур и давлений, в сверхсильных магнитных полях и т.п. Все эти задачи требуется решать для обеспечения технологического прогресса, в частности, для создания и доведения до практической применимости двигателей и энергетических установок, работающих на новых принципах; создания новых материалов с заданными свойствами и пр.
Моделирование построения и работы наносистем, необходимое для успешного развития нанотехнологий и создания механизмов, решающих важные для человека задачи при помощи манипулирования отдельными молекулами и атомами.
Моделирование биохимических процессов, связанных с функционированием различных белков, обменом веществ между живой клеткой и окружающей средой, модификацией генетической информации, активизацией и дезактивацией генов. Понимание их механизмов поможет создать более эффективные и безопасные лекарства против многих болезней, в том числе, плохо поддающихся лечению современными средствами, получить новые, более стойкие и полезные сорта и виды используемых в сельском хозяйстве растений и животных, а в перспективе — продлить жизнь каждого человека до ее биологических пределов.
Моделирование сложных экологических, климатических, экономических и социальных систем. Оно позволит более глубоко понять законы их развития, выработать механизмы разрешения и предотвращения кризисов и катастроф, приносящих серьезный ущерб отдельным людям и обществу в целом, а также поможет человечеству более ответственно относиться к среде его существования и заложить основы для гармоничного и стабильного развития в дальнейшем.
1. Введение На современном уровне развития общества большое значение в его технологической инфраструктуре играют информационные системы. На них перекладывается все больше функций по хранению, обработке и адекватному представлению информации, являющейся одним из базовых элементов сегодняшней экономической и культурной жизни человечества. Удивительно, но до сих пор нет эффективных технологий разработки информационных систем, работающих правильно, т. е. так, как того ожидают их пользователи. Это обусловлено двумя причинами. Во-первых, само понятие правильности такой системы неуловимо и неформально, поскольку оно тесно связано с набором нечетких, противоречивых и меняющихся со временем ожиданий и потребностей людей, общающихся с ней. Во-вторых, известные недостатки человеческой природы не позволяют разработчикам программ писать их без ошибок, даже когда они четко понимают поставленную задачу и имеют математически выверенное ее решение. Итеративная разработка с использованием тестирования является единственным практически работающим способом создания достаточно качественного программного обеспечения (ПО), хотя и не корректного формально, но достаточно хорошо удовлетворяющего потребности пользователей. В ходе такой разработки проектирование и написание кода чередуется с проверками работоспособности и правильности результатов работы проектируемых и разрабатываемых компонентов.
69
70
Все эти задачи решаются при помощи использования тех или иных методов математического моделирования и дают результаты, проверка правильности которых связана с большими затратами ресурсов и усилий многих людей, а иногда и вообще невозможна или создает очень серьезные угрозы для отдельных людей или общества в целом.
0 1 знак
мантисса
Знаковый бит S, экспонента E и мантисса M числа x определяют его значение по следующим правилам. x = (-1)S·2e·m, где o S — знаковый бит, 0 для положительных чисел, и 1 для отрицательных; o если E > 0, то e = E-2(k-1)+1; иначе, если E = 0, e = -2(k-1)+2; число (2(k-1)-1) называется смещением экспоненты (bias);
В данной работе изучаются проблемы проверки корректности реализаций математических функций, работающих с числами с плавающей точкой, анализируются имеющиеся достижения в этой области и предлагается метод разработки тестов для таких функций на основе формальных спецификаций их поведения. Полученный метод базируется на технологии UniTESK [1-3], дополняя ее методиками формирования точных требований к реализациям математических функций и выбора тестовых данных для их тестирования.
o если 0 < E < 2k-1, то m имеет двоичное представление 1.M, т.е. целая часть m равна 1, а последовательность цифр дробной части совпадает с последовательностью бит M; если же E = 0, то m имеет двоичное представление 0.M, такие числа (с нулевой экспонентой) называются денормализованными.
Сперва рассмотрим проблемы, с которыми сталкиваются исследования в этой области.
Максимальное возможное значение экспоненты E = 2k-1 зарезервировано для представления положительной +∞ и отрицательной -∞ бесконечностей и специального значения NaN (not-a-number), которое возникает, если результат выполняемых действий нельзя корректно представить ни обычным числом, ни бесконечностью, например, 0/0 или (-∞) + (+∞). +∞ имеет нулевой знаковый бит, максимальную экспоненту и нулевую мантиссу; -∞ отличается только единичным знаковым битом. Любое число, имеющее максимальную экспоненту и ненулевую мантиссу, считается представлением NaN. Все такие числа называются исключительными.
Стандарты IEEE 754 и IEEE 854 определяют несколько возможных типов чисел с плавающей точкой, из которых чаще всего используются числа однократной точности (single precision), числа двойной точности (double precision) и числа расширенной двойной точности (double-extended precision). Для чисел однократной точности n = 32 и k = 8. Соответственно, для мантиссы используется 23 бита и смещение экспоненты равно 127. Для чисел двойной точности n = 64 и k = 11. Для мантиссы используется 52 бита и смещение экспоненты равно 1023. Для чисел расширенной двойной точности определенные значения k и n
2. Проблемы корректного вычисления функций Основные проблемы корректного вычисления математических функций связаны с дискретностью представления действительных чисел в компьютерах. Чтобы иметь возможность эффективно выполнять операции с действительными числами, они представлены в виде так называемых чисел с плавающей точкой, формат и правила действий над которыми определены в стандартах IEEE 754 [4] (он же — IEC 60559 [5]) и IEEE 854 [6]. IEEE 754 определяет представление двоичных чисел с плавающей точкой, IEEE 854 обобщает его, определяя и десятичные числа с плавающей точкой. Однако, поскольку в большинстве случае на практике используется двоичное представление чисел, мы будем рассматривать только его.
2.1. Числа с плавающей точкой Двоичное число с плавающей точкой имеет следующую структуру [4,6,7]. Число представлено в виде набора из n бит, из которых первый бит является знаковым битом числа, следующие k бит отданы под представление его экспоненты, а оставшиеся (n-k-1) бит представляют его мантиссу.
71
экспонента
n-1
Рисунок 1. Битовое представление чисел с плавающей точкой.
Тем не менее, есть возможность повысить надежность и правильность работы программных систем, выполняющих моделирование такого рода. Это может быть сделано за счет формальной проверки корректности работы библиотечных математических функций, на которые во многом это ПО опирается. Уверенность в надежности фундамента, на котором построены такие системы, позволит разрабатывать их более качественно и с меньшими усилиями, сосредоточившись на поиске и исправлении ошибок в других компонентах.
k k+1
72
не фиксируются в стандартах, вводятся лишь ограничения 128 n 80 и k 15. В процессорах Intel 32-битной архитектуры используются значения n = 80 и k = 15. При этом для мантиссы используется 64 бита и смещение экспоненты равно 16383.
o предшествующее 1 число, представимое в таком виде 1-2-24 = 2-24·(224-1) = (-1)0·2126-127·1.111111111111111111111112 0 01111110 11111111111111111111111
числа, имеющие одну экспоненту и разные мантиссы 0
денормализованные числа
числа, имеющие нулевую мантиссу и различные экспоненты
Рисунок 2. Примерная картина распределения чисел с плавающей точкой.
Примеры чисел с плавающей точкой.
Числа однократной точности o 0 = (-1)0·20-126·0.02 имеет представление 0 00000000 00000000000000000000000 o 1 = (-1)0·2127-127·1.02 имеет представление 0 01111111 00000000000000000000000 o -1710 = (-1)1·2131-127·1.00012 имеет представление 1 10000011 00010000000000000000000 o 0.7510 = (-1)0·2126-127·1.12 имеет представление 0 01111110 10000000000000000000000 o самое маленькое положительное число, представимое в таком виде (денормализованное) 2-149 = (-1)0·20-126·0.000000000000000000000012 0 00000000 00000000000000000000001 o самое маленькое положительное число, представимое в нормальном виде 2-126 = (-1)0·21-127·1.02 0 00000001 00000000000000000000000 o самое большое положительное число, представимое в таком виде 2104·(224-1) = (-1)0·2254-127·1.111111111111111111111112 0 11111110 11111111111111111111111 o +∞ имеет представление 0 11111111 00000000000000000000000 o следующее за 1 число, представимое в таком виде 1+2-23 = (-1)0·2127-127·1.000000000000000000000012 0 01111111 00000000000000000000001
Числа двойной точности o 0 = (-1)0·20-1022·0.02 имеет представление 0 00000000000 0000000000000000000000000000000000000000000000000000 o 1 = (-1)0·21023-1023·1.02 имеет представление 0 01111111111 0000000000000000000000000000000000000000000000000000 o -1710 = (-1)1·21027-1023·1.00012 имеет представление 1 10000000011 0001000000000000000000000000000000000000000000000000 o 0.7510 = (-1)0·21022-1023·1.12 имеет представление 0 01111111110 1000000000000000000000000000000000000000000000000000 o самое маленькое положительное число, представимое в таком виде (денормализованное) 2-1074 = (-1)0·20-1022·2-52 0 00000000000 0000000000000000000000000000000000000000000000000001 o самое маленькое положительное число, представимое в нормальном виде 2-1022 = (-1)0·21-1023·1.02 0 00000000001 0000000000000000000000000000000000000000000000000000 o самое большое положительное число, представимое в таком виде 2971·(253-1) = (-1)0·22046-1023·(253-1)·2-52 0 11111111110 1111111111111111111111111111111111111111111111111111 o +∞ имеет представление 0 11111111111 0000000000000000000000000000000000000000000000000000 o следующее за 1 число, представимое в таком виде 1+2-52 = (-1)0·2127-127·(1+2-52) 0 01111111111 0000000000000000000000000000000000000000000000000001 o предшествующее 1 число, представимое в таком виде 1-2-53 = 2-53·(253-1) = (-1)0·21022-1023·(253-1)·2-52 0 01111111110 1111111111111111111111111111111111111111111111111111
интервалы между нормализованными числами с соседними экспонентами различаются в 2 раза
В дальнейшем будем называть действительные числа, точно представимые в виде чисел с плавающей точкой, просто представимыми. Ясно, что не каждое действительное число представимо. Например, π — иррационально, а все представимые числа рациональны, точнее даже двоично-рациональны. 1/3 тоже не представимо, поскольку не является двоично-рациональным числом. Числа 210000000 и 2-10000000 не представимы, хотя и двоично-рациональны, потому что первое слишком велико, а второе слишком мало по абсолютной величине, даже для представления с четырехкратной точностью. Числа, находящиеся слишком близко к представимым, тоже не представимы, например, для представления 1+2-1000 нужна мантисса из 1000 нулей и одной единицы. Заметим, что существует число с плавающей точкой -0, отличающееся от 0. Стандарт IEEE 754 требует, однако, считать их равными. Кроме того, ни одна из операций над числами с плавающей точкой, описанных в этом стандарте, не должна давать 73
74
в результате -0, за исключением квадратного корня из -0. При сложении, вычитании, умножении, делении, вычислении остатка от деления и преобразованиях типов всегда в случае нулевого результата возвращается 0.
2.2 Требования стандартов к реализациям математических функций
Arenaire [12], совместно проводимом INRIA, CNRS и Высшей Нормальной школой Лиона, Франция. В результате активно разрабатывается набор стандартов ICO/IEC 10967 [13-15], формулирующий естественные ограничения на работу реализаций большинства математических функций. Эти ограничения касаются нескольких аспектов.
Поскольку не все действительные числа представимы, возникает проблема представления результатов математических функций в тех случаях, когда такой результат не представим.
Возможные погрешности вычисления функций выражены в терминах единиц последнего разряда (unit in the last place, ulp) [7]. Наилучшее приближение дало бы точность в 0.5 ulp, т.е. вычисленный результат отличался бы от точного значения функции не более чем на половину единицы последнего разряда мантиссы результата. Однако природа математических функций такова, что такая точность не является практически обоснованной во многих случаях, хотя ее достижение потребовало бы значительных усилий от разработчиков библиотек. Это связано с тем, что число с плавающей точкой является приближенным представлением любого действительного числа, к которому оно является ближайшим. Таким образом, уже в значениях аргументов функции может иметься погрешность, которую ее вычисление не в силах исправить. Для широко используемых математических функций были проведены оценки возрастания погрешности при их вычислении, и на основании этих оценок были сформулированы более практичные требования к точности вычислений, ограничивающие погрешность результата величиной от 0.5 ulp до 2 ulp, в зависимости от функции [14].
Стандарты серии ISO/IEC 10967 требует от реализации математической функции сохранения знака ее точного значения для данного значения параметра. Также требуется, чтобы во всех интервалах монотонности функции ее реализации были монотонны таким же образом, т.е. там, где сама функция убывает, ее численная реализация должна убывать, а там, где функция возрастает, — возрастать. Исключением из этого правила являются тригонометрические функции в области больших значений аргумента, где интервал смены знака и интервал монотонности становятся сравнимы с единицей последнего разряда аргумента.
ISO/IEC 10967 требует соблюдения специфических требований при вычислении функций в окрестностях точек, где они имеют известные представимые значения. Например, реализация экспоненты для значений аргументов, достаточно близких к 0, должна возвращать в точности 1. Это требование связано с тем, что плотность чисел с плавающей точкой в окрестности 0 гораздо больше, чем их плотность в окрестности 1 — между 1 и ближайшим к нему числом с плавающей точкой умещается много чисел, близких к 0. Для двойной точности ближайшее к 1 число равно 1-2-53, а ближайшее к 0 — это 2-1074.
Казалось бы, естественно потребовать, чтобы реализация функции возвращала в качестве результата число с плавающей точкой, являющееся ближайшим к результату точного вычисления функции для тех же самых значений аргументов. Однако, ни стандарты IEEE 754 и IEEE 854, ни стандарт языка C ISO/IEC 9899 [8], ни стандарт переносимого интерфейса операционной системы IEEE 1003.1 [9] (известный как POSIX), описывающие библиотеку математических функций языка C не фиксируют такого требования для большинства функций. Стандарты IEEE 754 и IEEE 854 описывают работу только сложения, умножения, вычитания и деления чисел с плавающей точкой, а также вычисления остатка от деления, извлечения квадратного корня и преобразований между типами с плавающей точкой и между ними и целочисленными типами. Соответственно, только для этих операций требуется возвращать результат, полученный из точного приведением к ближайшему представимому числу согласно действующему режиму округления. Возможны 4 режима округления: просто к ближайшему, к 0, к +∞ и к -∞. Кроме того, эти стандарты требуют аккуратного выставления флагов переполнения, слишком маленького результата или неточного результата при работе этих операций. Стандарт C ссылается на требования IEEE 754, добавляя только ограничения на значения результатов ряда функций для некоторых значений параметров (например, exp(0) = cos(0) = 1, а sin(0) = tg(0) = 0). Стандарт POSIX, в свою очередь, ссылается на требования стандарта языка C, добавляя описание поведения реализаций математических функций в случае возникновения переполнения или слишком маленьких результатов и для тех значений параметров, где соответствующая функция не определена. Отсутствие ограничений на точность вычисления математических функций может привести к накоплению погрешностей и серьезным ошибкам при многократном использовании этих функций в приложениях для математического моделирования, и, соответственно, неверным результатам работы таких приложений. В последние 5-10 лет появились предложения стандартизовать необходимые для аккуратного моделирования требования к реализациям математических функций [10,11]. Многие инициаторы этой деятельности работают в проекте
75
76
1.01100001001110010101010111011100100000000010111110002·2-35 получим 60 -10111.1111000000101111100110111010111101100000001101010 1 0011…2 Обозначение 160 означает, что единица повторяется 60 раз. Таким образом, для получения корректно округленного значения нужно вычислять логарифм в этой точке с относительной погрешностью, не превосходящей 2-113.
значения реализации функции для чисел с плавающей точкой
точный график функции
0
Рисунок 3. График функции, имеющей ненулевое значение в 0.
2.3. Дилемма составителя таблиц Сформулированные требования к точности вычисления математических функций приводят к так называемой дилемме составителя таблиц (Table Maker’s Dilemma) [16,17]. Эта проблема состоит в том, что для выбора правильно округленного ближайшего числа с плавающей точкой при приближенных вычислениях иногда нужно вычислить много дополнительных бит мантиссы результата, значительно больше, чем имеется в рассматриваемом типе чисел с плавающей точкой. Для иллюстрации дилеммы составителя таблиц приведем следующий пример. Пусть вычисляется функция sin для двоичных чисел с плавающей точкой, имеющих 6 битов мантиссы. Синус числа 11.10102 = 3.62510 (выделены биты мантиссы) равен 0.011101101111110...2 = 0.063225984913…10 (снова выделены биты мантиссы). Приближенное вычисление 6-ти бит мантиссы результата может дать как 0.01110112, так и 0.01111002, поскольку точное значение очень близко к их среднему арифметическому. Только получив точный 14-й бит, мы сможем уверенно выбрать первое из них в качестве значения, ближайшего к точному результату. результаты приближенных вычислений с уменьшающейся погрешностью точное значение функции
соседние числа с плавающей точкой и их среднее арифметическое
Рисунок 4. Дилемма составителя таблиц. Чтобы определить ближайшее число с плавающей точкой, погрешность вычислений иногда должна быть значительно меньше половины расстояния между соседними такими числами.
Приведем более реалистичный пример для чисел с двойной точностью, имеющих 52 бита в мантиссе. Вычисляя значение натурального логарифма для
77
Описанные примеры показывают, что для выбора ближайшего числа с плавающей точкой иногда нужно использовать гораздо более точные вычисления, чем это позволяет сделать тип таких чисел. Аналогичные примеры существуют и для других режимов округления. Если используется режим округления к 0, к + или к -, а точное значение функции лежит очень близко к представимому числу, необходимо добиться погрешности настолько маленькой, чтобы точно определить, превосходит оно это число или нет. Например, натуральный логарифм для числа 110101100.010100001011010000001001110010001010111011102 равен 110.00001111010100101111001101111010111011001111110011 161 0101...2. Для практически всех часто используемых функций их значения в «обычных» (т.е., не равных 0, 1 или 2) двоично-рациональных числах не являются рациональными, и поэтому не могут ни быть представимыми, ни лежать в точности посередине между двумя представимыми числами. Из этого в силу конечности множества представимых чисел следует, что для каждой функции есть такое число ε > 0, что вычисляя значения этой функции с погрешностью, не превосходящей ε, можно всегда точно определить корректное округление, являющееся представимым числом. Однако вычислять функцию с такой точностью для всех значений аргумента может оказаться слишком неэффективно. Например, для натурального логарифма на числах двойной точности при произвольном режиме округления такое ε можно взять равным 2-118 [17]. Однако реально такая точность нужна только для единственного значения аргумента, во всех остальных случаях можно использовать меньшую. Для подавляющего же большинства представимых чисел двойной точности корректное округление их логарифма можно получить, вычисляя его с погрешностью, не превосходящей 2-54. Дилемма составителя таблиц приводит к необходимости организации значительно более точных вычислений, чем это позволяют сделать стандартные типы чисел с плавающей точкой, как при построении правильных реализаций математических функций, так и проверке их корректности. В то же время, проводить настолько точные вычисления для всех значений аргументов очень неэффективно. Для повышения эффективности вычислений нужно уметь выбирать их точность в зависимости от аргументов функции.
78
O’Leary с соавторами для Nurpl в 1994 [28], Miner для PVS в 1995 [29,30], Carreno для HOL в 1995 [30,31], Moore с соавторами для ACL2 в 1996 [32], Harrison для HOL в 1996 [33].
3. Обзор работ по проверке корректности реализаций математических функций Методам вычисления математических функций посвящено огромное количество работ. Одним из классических трудов на эту тему является сборник статей под редакцией Abramowitz и Stegun [18], хотя он был выпущен уже довольно давно и частично устарел. Более современное изложение методов вычисления элементарных функций (являющихся только подмножеством рассмотренных в [18]) можно найти в книге Muller [19]. Значительно реже встречаются исследования, в которых не только формулируются методы вычисления каких-либо функций, но и доказывается их корректность. Под корректностью имеется в виду достижение определенной точности результатов при определенных значениях параметров метода. Только часть из этих работ посвящена вычислениям на числах с плавающей точкой, представление которых определяется в IEEE 754. Большинство из таких исследований связано с корректностью работы алгоритмов вычисления функций, реализованных в специализированном аппаратном обеспечении. Помимо точности вычислений здесь приходится учитывать разнообразные режимы округления, определяемые в IEEE 754, корректность выставляемых флагов, например, переполнения, а также правильность работы алгоритма на специальных значениях — бесконечностях и NaN (см. далее). Гораздо реже встречаются исследования по строгому тестированию правильности реализаций математических функций, работающих с числами с плавающей точкой. Таким образом, все имеющиеся работы по проверке правильности работы реализации математических функций можно разделить на следующие группы.
Работы по формальной верификации определенных алгоритмов.
Работы по тестированию реализаций математических функций.
3.1. Работы по формальной верификации определенных алгоритмов Формальная верификация корректности алгоритмов вычислений математических функций чаще всего проводится при проектировании блоков вычислений с плавающей точкой универсальных процессоров или при разработке специализированных вычислительных процессоров [28,32,34-52,54]. Основой для такой верификации всегда является точное знание реализованного алгоритма и формализация основных требований стандарта IEEE 754, необходимая для их строгой проверки. Формализация этих требований проводилась несколько раз в различных формализмах: в языках формальных спецификаций Z [20], Barrett в 1989 [21], и VDM [22], Wichmann в том же году [23], в формализмах инструментов автоматизации доказательств Nurpl [24], PVS [25], HOL [26] и ACL2 [27] —
79
В работах Verkest с соавторами [34] и Cornea-Hagesan [35,36] все доказательства выполнялись вручную. Но в большей части исследований такого рода, ввиду значительной сложности верификации практически важных систем, применяются только формализации, которые могут использоваться инструментами для автоматизированного доказательства теорем. Статьи [32,34,37-41] имеют дело только с алгоритмами умножения и/или деления чисел с плавающей точкой и представляют примеры верификации корректности таких алгоритмов по отношению к требованиям IEEE 754. В работах [35,36,42-48] верифицируются также алгоритмы вычисления квадратного корня, иногда еще и вычисление остатка от деления для чисел с плавающей точкой и преобразования между различными такими типами и между ними и целыми числами. В связи с тем, что в стандарте IEEE 754 определены только арифметические действия, вычисление остатка от деления и квадратного корня, проверке правильности вычисления других функций посвящено значительно меньше работ. В [49,50] описываются практические примеры формальной верификации алгоритмов, вычисляющих экспоненциальную функцию. В статье [51] верифицировалось вычисление синуса и косинуса, а в [52] — натурального логарифма. Все четыре работы были выполнены Harrison и его коллегами. В работе [53] нескольких исследователей из проекта Arenaire [12], представлен пример верификации вычисления натурального логарифма с помощью специализированного инструмента Gappa, позволяющего автоматизировать доказательства, касающиеся свойств элементарных функций. Других примеров верификации математических функции в доступной литературе найти не удалось. В перечисленных работах верифицировались отдельные элементы и блоки процессоров компаний Intel (Pentium II, Pentium III, Pentium 4 и Itanium [35,36,41-43,46,47,49-52]) AMD (K5, Athlon [32,44,45]), IBM (Power4 [48]). При этом использовались следующие инструменты автоматизации доказательств: HOL ([43,49-52]) и близкий к нему Nurple ([29]), PVS ([38,40,54]), ACL2 ([32,44,45,48]) и его предыдущая версия Nqthm ([34]). Помимо этого применялись различные комбинации автоматизированного доказательства теорем с проверкой моделей (model checking) и символической проверкой эквивалентности моделей ([37,39,41,42,46,47]). По-видимому, до сих пор не было удачных попыток полностью проверить соответствие блока вычислений с плавающей точкой стандарту IEEE 754. Во всех найденных работах либо проверяются не все операции, определяемые стандартом, либо не проверяется их работа для специальных чисел с плавающей точкой — денормализованных, -0, бесконечных и NaN. Работа [54], похоже, единственная, в которой систематически рассматривается поведение части описанных в IEEE 754 операций на таких исключительных значениях.
80
удовлетворяющие некоторым шаблонам — например, в которых нулевые и единичные биты мантиссы чередуются, или в которых мантисса содержит ровно 7 единиц.
Можно также отметить, что в процессоре, верифицированном в проекте, описанном в [42] впоследствии была найдена ошибка в операции преобразования числа с плавающей точкой в целое (так называемый FIST bug [55]). Это показывает, что формальная верификация сложной системы, будучи тоже достаточно сложной деятельностью, из которой нельзя исключить участие людей, сама по себе подвержена ошибкам.
o Тестовый набор UCBTEST [67] предназначен для тестирования базовых арифметических действий и достаточно широкого набора математических функций. Он оформлен как набор программ на разных языках, включая Fortran и C, и наборов предопределенных входных данных для разных функций. В каждом тесте проверяется, что для заданных значений параметров данная функция возвращает число с плавающей точкой, ближайшему к точному значению функции.
3.2. Работы по тестированию реализаций математических функций В Интернет можно найти огромное количество различных программ для тестирования функций, работающих с числами с плавающей точкой, см. например, [56]. К сожалению, подавляющее большинство таких тестов крайне несистематично и проверяет какой-то один аспект вычислений, реже — два-три таких аспекта.
o Тестовый набор ELEFUNT [68], основанный на книге [69], также содержит тесты для многих математических функций в виде программ на C и Java и текстовых файлов с тестовыми данными и ожидаемыми результатами. Этот и предыдущий тестовый набор построены на основе проверки значений, возвращаемых реализациями функций для некоторых наборов аргументов. Методика выбора этих наборов аргументов, скорее всего, использовала несколько разных соображений.
Как указывается в [56] (см. также иллюстрации из [57,58]), несмотря на то, что стандартизация вычислений с плавающей точкой началась около 20 лет назад, до сих пор многие поставщики библиотек и аппаратного обеспечения не придерживаются имеющихся стандартов достаточно строго, поэтому тесты на правильность поведения реализаций математических функций по-прежнему необходимы.
Среди наиболее систематичных работ по тестированию вычислений с плавающей точкой можно назвать следующие.
имеющие ровно один бит в мантиссе, и пр. (см. ниже).
o В работе [59] описывается самый первый из известных систематических тестовых наборов для проверки корректности реализации операций над числами с плавающей точкой. Он появился еще до введения в действие стандарта IEEE 754, сделан в виде набора программ на Fortran и предназначен для тестирования только сложения, вычитания, умножения и деления. o Специально для проверки на соответствие IEEE 754 был разработан тестовый набор, который описан в статьях [60,61] и может быть получен с сайта [62]. В этом наборе проверяются все требования стандарта к арифметическим операциям, вычислению квадратных корней и взятию остатков, а также преобразования между типами чисел с плавающей точкой и целыми.
o Другой подход к построению тестов для операций IEEE 754 используется в среде FPgen [65,66]. Здесь, помимо специальных значений, в качестве данных используются числа с плавающей точкой, тестовых 81
Выделялись особые значения чисел с плавающей точкой: 0, -0, NaN +∞, -∞, минимальное положительное, максимальное положительное, числа,
Работы по тестированию на соответствие стандарту IEEE 754.
o Программа PARANOIA [63,64] была создана одним из авторов стандарта IEEE 754 Кэханом (W. Kahan) и остается довольно популярным средством проверки на соответствие ему, хотя такая проверка менее тщательна, чем с помощью тестового набора, описанного выше. Она также проверяет только базовые арифметические операции.
Работы по тестированию широкого набора математических функций.
Выделялись значения аргументов, значение функции для которых может быть точно представлено числом с плавающей точкой (например, exp(0) = 1, cos(0) = 1 и пр.).
Некоторые значения аргументов выбирались, по-видимому, случайно или из соображений, связанных со структурой известных алгоритмов для вычисления элементарных функций. Например, в ряде алгоритмов вычисления логарифма сначала значение аргумента при помощи умножения или деления на 2 приводится к интервалу (0.5, 1]. Соответственно, выбираются границы этого интервала и нескольких соседних с ним.
o Аналогичные подходы — использование ряда специальных значений, границ интервалов, определяемых часто используемыми алгоритмами вычисления данной функции, и случайных значений — применялись для построения более полных тестовых наборов, например, набора Беркли [70], а также в статье [71]. Отдельно стоит отметить работы в рамках проекта Arenaire [16,17,72], посвященные дилемме составителя таблиц и поиску чисел, для которых корректное вычисление функций с заданной точностью наиболее трудоемко. Эти числа можно использовать в
82
качестве «неудобных» тестовых значений для практически любой реализации соответствующей функции.
Постусловие каждой функции анализируется с тем, чтобы выделить из него различные возможные варианты ее поведения. Они чаще всего соответствуют ветвлениям в теле постусловия и различным выражениям, описывающим ограничения на результат функции. Такие различные варианты поведения функции называются ее функциональными ветвями. Набор функциональных ветвей определяет набор ситуаций, в которых реализация обязательно должна быть протестирована для того, чтобы проверить хотя бы один раз все выписанные ограничения.
Помимо функциональных ветвей могут существовать другие ситуации, в которых тестирование данной реализации необходимо. Часть из этих ситуаций может быть получена более тонким анализом требований, а другая часть — на основе анализа возможных ошибок в конкретной реализации. Такие ситуации также могут быть описаны в спецификациях с помощью специальных конструкций.
На основе полученного набора ситуаций, в которых поведение реализации функции должно быть проверено, разрабатывается тестовый сценарий. Он определяет множество наборов аргументов функции, с которыми она будет вызываться во время тестирования. Это множество должно обеспечивать покрытие всех выделенных ситуаций, что контролируется при помощи автоматического построения отчетов о покрытии тестовых ситуаций, определенных в спецификации. Возможности технологии UniTESK по автоматическому построению тестовой последовательности для тестирования математических функций не требуются. Единственное возможное исключение — наличие гипотез о зависимости работы реализаций математических функций от каких-то элементов внутреннего состояния тестируемой системы. В этом случае дополнительно должна быть построена обобщенная модель состояния, каким-то образом учитывающая те элементы, которые, как предполагается, могут влиять на работу функций.
В рамках того же проекта был разработан инструмент MPCheck [73] для тестирования корректности реализации элементарных функций с точки зрения сохранения монотонности, симметрий, ограничений на область значений и корректности округления. Доступная литература по исследованиям, посвященным проверке корректности реализации математических функций, показывает, что формальные спецификации не используются для разработки тестов для таких функций, хотя все предпосылки и технологические возможности для применения такого подхода имеются. Математические функции имеют четко определенное поведение, которое практически однозначно понимается разработчиками ПО и легко может быть зафиксировано в виде международных стандартов, поэтому разработка их спецификаций не является слишком трудоемкой задачей. Использование формальных спецификаций также дает ряд преимуществ, связанных с повышением удобства сопровождения и снижением трудоемкости модификации больших тестовых наборов, по сравнению с традиционными методами разработки тестов. Все систематические тестовые наборы для тестирования библиотек математических функций достаточно объемны, поэтому построение тестов для них на основе формальных спецификаций способно реализовать эти преимущества.
4. Предлагаемый подход Подход к тестированию реализаций математических функций, предлагаемый в данном исследовании, основан на технологии UniTESK, использующей формальные спецификации требований к программному обеспечению для автоматизированного построения тестов на соответствие им. Основные элементы технологии UniTESK [1-3], применительно к реализациям математических функций, таковы.
Требования к поведению тестируемой системы представляются в виде формальных спецификаций, которые состоят из следующих частей: o предусловия, описывающие области определения функций; o постусловия, описывающие функциями результатов;
условия
корректности
возвращаемых
o инварианты типов данных, описывающих условия целостности данных.
Чтобы сделать спецификации независимыми от конкретной сигнатуры функции (и, возможно, от языка программирования, на котором она реализована), разрабатывается слой адаптеров или медиаторов, связывающих спецификации и реализации соответствующих функций друг с другом.
83
Чтобы адекватно применять технологию UniTESK для построения тестов для математических функций, необходимо ответить на два вопроса: какие требования должны предъявляться к поведению их реализации, т.е. что именно должно быть написано в спецификациях; и на каких значениях параметров функций должно проводиться тестирование. В рамках предлагаемого подхода были разработаны метод определения требований к реализации конкретной математической функции и метод выбора тестовых данных для тестирования конкретной функции.
84
не попадает в диапазон чисел однократной точности, поэтому реализация экспоненты для таких чисел должна возвращать +∞.
4.1. Метод определения требований к математическим функциям
Данный метод определения требований к поведению реализаций математических функций заимствует часть идей из стандарта ISO 10967 [13-15] и работ [10,11], посвященных вопросу разработки серии стандартов с повышенными требованиями к корректности вычисления математических функций и удовлетворяющих им библиотек. Некоторые элементы предлагаемого метода являются новыми и не встречаются в доступной литературе.
o Нужно наиболее естественным образом определить значения функции для особых значений аргумента: -0, +∞, -∞. Обычно достаточно определять их как пределы, если те существуют, иначе как NaN. o Значение функции для значения аргумента NaN должно быть равно NaN. o Для некоторых значений аргумента значения функции известны точно. Если оба значения представимы, естественно потребовать от реализации этой функции возвращать именно точное ее значение в таких точках. Например, exp(0) = cos(0) = ch(0) = 1, sin(0) = tan(0) = arcsin(0) = 0 и т.п.
Требования к реализации математической функции могут быть разделены на несколько аспектов, которые должны быть рассматриваться отдельно.
Область определения функции и особые точки функции.
o Кроме этого, если в такой точке производная функции равна 0, то для любого аргумента из некоторой ее окрестности реализация должна возвращать то же самое значение.
o Для всех значений аргументов, где математическая функция определена, ее реализация должна возвращать некоторый результат, который может быть равен +∞ или -∞, если значение самой функции находится за пределами интервала чисел с плавающей точкой, но не должен быть NaN.
o Так как около 0 плотность чисел с плавающей точкой больше, чем около любого другого значения, если значение функции в 0 не равно 0, даже если ее производная там ненулевая, должно быть выполнено то же самое правило: в некоторой окрестности 0 для всех чисел с плавающей точкой значение ее реализации должно быть одинаковым. Например, для экспоненты e0 = 1, при этом (ex-1) < 2-24 при x > 1 и (x-1) < ln(1+2-24) = 5.9604642999…·10-8, поэтому для всех таких x, которых довольно много, реализация экспоненты с однократной точностью должна возвращать 1.
o Для всех значений, для которых математическая функция не определена (в том числе, и для ее особых точек), но имеет однозначно определенный предел, может быть, равный +∞ или -∞, реализация должна возвращать значение этого предела. o Если функция имеет особенность в точке 0, не имеет там предела, равного +∞ или -∞, нужно рассматривать односторонние пределы функции. Значение реализации функции в 0 нужно считать равным ее пределу при x +0, если он существует, а значение в -0 — пределу при x -0, если он есть. Примером такой функции служит котангенс.
o В тех случаях, когда функция имеет горизонтальные асимптоты, необходимо аккуратно определить границы, после которых ее значение должно стать постоянным. Например, для значений x < ln(2-150) = -103.97207708399… значение ex становится ближе к 0, чем к какому либо еще числу, представимому с однократной точностью. Поэтому реализация экспоненты для таких аргументов должна возвращать 0.
o В остальных случаях должен возвращаться результат NaN. Особо нужно рассматривать такие значения аргументов, по поводу которых нет однозначного мнения о принадлежности их к области определения функции или о возможном продолжении ее в эту точку по непрерывности. Примером служит значение 00, которое иногда интерпретируется как 1, а иногда как NaN.
o Казалось бы, естественно предъявить аналогичные требования к функциям, имеющим негоризонтальные асимптоты или асимптотически близких к другим функциям. Например, ch(x) = (ex+e-x)/2 ~ ex для достаточно больших значений аргумента, или sin(x) ~ x при x ~ 0. Однако во многих случаях такое требование не может быть сформулировано достаточно аккуратно с учетом различных режимов округления, зафиксированных в IEEE 754. Дело в том, что даже очень маленькая разность между двумя асимптотически близкими выражениями может дать отличие в
o Для полюсов функции, где ее значение стремится к бесконечности, необходимо точно определить окрестности, в которых оно уже не является представимым. При наличии представимых чисел в такой окрестности, реализация функции для них должна возвращать значения +∞ или -∞ в соответствии со знаком точного значения. o Для функций, стремящихся к бесконечности при x +∞ или x -∞, должны быть точно определены пределы представимости их значений. За этим пределами реализация также должна возвращать +∞ или -∞ в соответствии со знаком точного значения. Например, для значений x > ln(2104·(224-1)) = 88,722839052… exp(x) 85
Специальные значения, значения в 0, касательные и асимптоты.
86
значимых битах мантиссы. Причина этого явления аналогична причине, порождающей дилемму составителя таблиц — слишком близкое расположение некоторых значений функции к представимым числам. Например, если рассматривать асимптотику ex ~ 1+x при x ~ 0 для чисел двойной точности, то при |x| < 2-28 разница между ex и 1+x уже меньше 0.5 ulp, однако встречаются гораздо более близкие к 0 числа, представимые с двойной точностью, для которых мантиссы ex и 1+x отличаются при выборе режимов округления к -∞ или к +∞. Скажем, для x = -52 -1.10000000000000000000000000000000000000000000000000012·2 exp(x) = 1.1111111111111111111111111111111111111111111111111101 053100…2·2-1, а1+x= 1.1111111111111111111111111111111111111111111111111100 1512·2-1. Цветом здесь выделены биты, не помещающиеся в мантиссу числа двойной точности. Похоже, что четкие требования можно предъявлять лишь по поводу соблюдения асимптотик вида f(x) ~ x или f(x) ~ -x (например, sin(x) ~ x и tg(x) ~ x при x ~ 0), поскольку при этом риск столкнуться с подобной проблемой невелик. Однако при этом нужно очень аккуратно вычислять границы действия подобных ограничений.
Небольшие отклонения от этого правила можно допускать только на границах интервалов монотонности в том случае, если эти границы не представимы. Другим исключением может быть поведение реализаций функций, часто меняющих характер монотонности (т.е. то убывающих, то возрастающих). Для значений аргументов, у которых единица последнего разряда превосходит длину интервала монотонности, проверять сохранение монотонности становится практически бесполезно. Примерами таких функций являются тригонометрические функции, а также функции Бесселя. o Знак значения реализации функции для некоторого аргумента должен совпадать со знаком значения самой функции. Если значение функции равно 0 для какого-то представимого числа, значение ее реализации для этого числа также должно быть равно 0. Это требование может не соблюдаться для тех значений аргументов, для которых единица последнего разряда становится больше величины интервала смены знака, например, для тех же тригонометрических функций и функций Бесселя при больших значениях аргументов.
Симметрии и периодичность. o Если математическая функция является четной или нечетной, этим же свойством должна обладать ее реализация.
Область значений функции. o Ограничения сверху или снизу на значения функции в рамках связной компоненты области ее определения нужно соблюдать и в ее реализации, в противном случае возможны различные неприятные эффекты. Например, при использовании в качестве результата реализации функции арктангенс на больших положительных аргументах числа с плавающей точкой, наиболее близкого к π/2 может оказаться, что это число больше π/2. Для чисел однократной точности это так: наиболее близкое к π/2 такое число — это 13176795/8388608 = 1.57079637050628662109375 > π/2. При этом tg(arctg(230)) = -2.2877…·107, что противоречит основному свойству обратных функций.
o В тех случаях, когда функция имеет симметрии относительно других представимых значений аргумента, например, выполняется правило f(1-x) = -f(x), выполнение аналогичного свойства для реализации не всегда возможно, поскольку число (1-x) может быть представимым при непредставимом x. Следует особо рассматривать такие случаи и накладывать ограничения, быть может, касающиеся только представимых значений аргумента с обеих сторон такого равенства.
o Поскольку денормализованные числа выделяются из всего множества чисел с плавающей точкой, необходимо аккуратно определить интервалы, на которых значения функции должны быть денормализованными.
o Вообще, все важные функциональные уравнения, которым удовлетворяет данная функция, должны быть проанализированы на предмет выявления необходимости соблюдать их для реализаций этой функции. Иногда нужно определить области аргументов, где выполнение этих требований имеет практическое значение. Пример свойств такого рода, выполнение которых нужно обеспечивать (с точностью до 0.5 ulp), если задействованные значения аргументов функции
o Если функция симметрична относительно непредставимого значения, как, например, синус — sin(π-x) = sin(x), его выполнение всегда может быть только приближенным.
Монотонность и сохранение знака. o На всех интервалах, где математическая функция монотонна, ее реализация должна иметь тот же вид монотонности. Это необходимо для адекватного отражения существенных свойств математических моделей в их численном представлении.
87
88
представимы,— это свойства Γ(1+x) = x·Γ(x) и Γ(1-x) = -x·Γ(-x) гаммафункции Γ(x). Иначе может нарушиться важное соотношение Γ(n) = (n-1)! для целых положительных n. o Для свойства периодичности функции остаются верными все те же аргументы. Если период представим, то можно проверять это свойство только для чисел, представимых вместе со своим сдвигом на число, кратное периоду. Если период не представим, реализация может быть только приблизительно периодична. Для значений аргумента, у которых единица последнего разряда больше, чем величина периода функции, проверка ее периодичности становится практически бесполезной.
изложенных в книгах [18,19] и многочисленных статьях, или на основе методов интервальных вычислений [87-90].
4.2. Метод выбора тестовых данных Предлагаемая методика выбора тестовых данных для тестирования математических функций основана на особенностях представление чисел с плавающей точкой, результатах работ [16,17,72] по вычислению «неудобных» значений аргументов и приведенном выше методе определения требований к реализациям таких функций, а также на технике построения тестов при помощи разбиения интервалов входных данных. Основные шаги этого метода состоят в следующем.
Корректное округление. Помимо всех перечисленных ограничений, нужно потребовать, чтобы результат, возвращаемый реализацией, получался из точного результата функции для данного аргумента при помощи принятой в текущей конфигурации процедуры округления. Иногда практически бессмысленно требовать точности 0.5-1 ulp, но во всяком случае погрешность выше 1.5-2.0 ulp должна рассматриваться как неточность соответствующей реализации. Стандарт IEEE 754 предписывает поддержку 4-х видов округления: к ближайшему представимому числу, к +∞, к -∞ и к 0. Часто три последних вида округления противоречат требований, сформулированным по остальным аспектам. В этом случае иногда можно принимать решение в зависимости от функции, потому что некоторые ее важные свойства могут нарушиться. Но чаще удобнее считать, что поддержка режима округления имеет более высокий приоритет, поскольку пользователь, применяющий такой режим, осведомлен о его последствиях.
При определении требований к реализации функции по описанной выше методике числа с плавающей точкой разбиваются на ряд интервалов, в рамках каждого из которых действуют свои собственные ограничения. Это, например, интервалы монотонности и сохранения знака, интервалы, на которых функция имеет постоянное значение, включая бесконечные, интервалы, на которых она не определена. Множество концов этих интервалов будем называть исходным множеством.
К исходному множеству добавляем числа 0, -0, +∞, -∞, минимальные и максимальные по абсолютной величине представимые числа, минимальное и максимальное денормализованные числа.
Полученное исходное множество разбивает числа с плавающей точкой на набор интервалов. Для выбранных числовых параметров n и k на каждом из этих интервалов возможные тестовые значения выбираются следующим образом. Сначала интервал разбивается на n более мелких интервалов, равных по количеству содержащихся в них чисел с плавающей точкой. При этом возникает (n+1) точка. Затем берутся все числа, лежащие в рассматриваемом интервале и отстоящие не более чем на k чисел с плавающей точкой от полученных точек. Получаемое множество точек назовем пробным множеством.
После определения требований они оформляются в виде формальных спецификаций. Для записи требований, касающихся корректного округления точного значения математической функции, необходимо уметь вычислять ее правильно округленные значения. Сделать это можно несколькими способами.
С помощью систем математических вычислений, например, Maple [74], Mathematica [75], MATLAB [76].
С помощью библиотек корректно округляемых функций, таких, как разработанная на основе работ Ziv [77] IBM Accurate Portable MathLib [78], GNU MPFR [79], разрабатываемая Sun libmcr [80] или библиотек SCSLib [81] и CRlibm [82], разрабатываемых в рамка проекта Arenaire [16,83-86].
Рисунок 5. Схема выбора тестовых данных на одном интервале с параметрами n=4, k=2.
Можно также разработать собственную реализацию функции на основе методов,
89
90
К пробному множеству необходимо добавить ряд значений, которые приводят к необходимости гораздо более точного вычисления значения рассматриваемой функции для них (см. выше о дилемме составителя таблиц и [17]). Кроме того,
стоит добавить значения, которые требуют не максимально точных вычислений, а, например, на один бит меньше максимума.
709.782712893383973096206318587064743041992187510 = 1011000101.11001000010111111101111101000111001111011112 Экспонента от этого числа равна 1.1111111111111111111111111111111111111111111100101010 00011011…2·21023. Здесь и далее выделены биты, не помещающиеся в мантиссу числа двойной точности.
Если есть гипотезы о возможных ошибках в вычислении функции, связанных с конкретной структурой значения ее аргумента, например, для чисел, в мантиссе (или экспоненте) которых ровно одна единица или единицы чередуются с нулями, нужно также добавить такие числа в пробное множество. Нужно попытаться сформулировать такие гипотезы в виде набора шаблонов, которым должны удовлетворять битовые представления этих чисел.
Экспонента от следующего представимого числа 709.78271289338408678304404020309448242187510 = 1011000101.11001000010111111101111101000111001111100002 уже не может быть представлена конечным числом двойной точности. Таким образом, для всех представимых чисел, больших или равных этому, реализация экспоненты должна возвращать результат +∞.
Наконец, в пробное множество нужно добавить несколько представителей NaN. Можно выбирать их по некоторым правилам, например, с минимальной и максимальной мантиссами, а также на основе различных комбинаций бит мантиссы.
Для числа 1011000101.11001000010111111101111101000111001111011112 при режимах округления к ближайшему, к 0 или к -∞ должно выдаваться 1.11111111111111111111111111111111111111111111001010102·21023, а при округлении к +∞ должен выдаваться результат 1.11111111111111111111111111111111111111111111001010112·21023.
Управлять количеством получаемых тестовых данных, тщательностью тестирования и временем выполнения тестов в рамках описанного метода возможно при помощи выбора параметров n и k для разбиения интервалов, а также с помощью использования разнообразных шаблонов, выбираемых на предпоследнем шаге.
4.3.
Примеры применения предложенных методов
В этом разделе будут рассмотрены два примера использования предложенных методик для формулировки требований и выбора тестовых данных для тестирования двух математических функций — экспоненциальной функции и тангенса. Оба примера будут рассматриваться для случая чисел двойной точности.
o Специальные значения. Естественно доопределить значения экспоненты в специальных точках следующим образом. exp(-0) = 1; exp(+∞) = +∞; exp(-∞) = 0; exp(NaN) = NaN.
4.3.1. Экспонента В данном разделе термин «экспонента» везде обозначает экспоненциальную функцию exp(x) = ex.
o Точные значения и их окрестности, окрестность 0. Известно только одно точное значение экспоненты: exp(0) = 1. Учитывая большую плотность чисел с плавающей точкой в 0, нужно определить окрестность 0, в которой экспонента должна возвращать 1.
Область определения и особые точки. Экспонента определена для всех действительных чисел, особых точек у нее нет. Она стремится к +∞ при x +∞, поэтому нужно найти соответствующие граничные значения — наибольшее число с плавающей точкой, для которого значение экспоненты еще может быть представлено конечным числом, и наименьшее такое, что экспонента от него должна быть равна +∞.
Для чисел, больших 0, имеем exp(1.11111111111111111111111111111111111111111111111111112·2-54) = 1.0000000000000000000000000000000000000000000000000000 01106001…2; exp(1.02·2-53) = 1.0000000000000000000000000000000000000000000000000000 1053100…2;
Максимальное конечное представимое в виде числа с двойной точностью значение равно 2971·(253-1). Его натуральный логарифм равен 709.7827128933839967322233899106571455...10, ближайшее к этому значению представимое число есть
91
Специальные значения, значения в 0, касательные и асимптоты.
exp(1.11111111111111111111111111111111111111111111111111112·2-53) = 1.0000000000000000000000000000000000000000000000000000 1105010…2; exp(1.02·2-52) = 1.0000000000000000000000000000000000000000000000000001
92
052100…2.
Поэтому, для чисел от 0 до (253-1)·2-106 = 1.11111111111111111111111111111111111111111111111111112·2-54 экспонента должна возвращать 1 при режимах округления к ближайшему, к 0 и к -∞. При округлении к +∞ для всех таких чисел, больших 0, результатом должно быть 1+2-52. Для чисел от 2-53 до (253-1)·2-105 = 1.11111111111111111111111111111111111111111111111111112·2-53 экспонента должна возвращать 1 при режимах округления к 0 и к -∞, а при режимах округления к ближайшему или к +∞, на этом интервале должно возвращаться следующее за единицей представимое число 1+2-52. Для значения аргумента 2-52 должно возвращаться 1+2-52 при режимах округления к ближайшему, к 0 и к -∞, а при округлении к +∞ — 1+2-51.
exp(-1.00000000000000000000000000000000000000000000000000012·2-54) = 1.1111111111111111111111111111111111111111111111111111 0152001…2·2-1;
Поэтому, для -(1+2-52)·2-53 экспонента должна возвращать 1-2-52 при округлении к -∞ или 0, и 1-2-53 при округлении к ближайшему или +∞. На интервале от -2-53 до -(1+2-52)·2-54, включая его концы, нужно возвращать 1-2-53 при округлении к -∞, 0 или ближайшему, и 1 при округлении к +∞. Для чисел от -2-54 до 0 нужно возвращать 1-2-53 при округлении к -∞ или 0 (кроме 0, для которого результат всегда 1) и 1 при округлении к ближайшему или +∞.
1-2-52 1-2
-53
1-2
-53
к +∞
-53
1-2-52
1-2-53
1-2-53
1-2
-53
-53
от -2 до -(1+2-52)·2-54 от -2 0
-54
до -2
-1074
1-2
-53
1
1
1-2
1
1
1
1
1
от 2-53 до (253-1)·2-105
1
1
1+2-52
1+2-52
2-52
1+2-52
1+2-52
1+2-52
1+2-51
exp(-1011101001.00100010000110101010010110100110000010100102 0.145011…2·2-1075 exp(-1011101001.00100010000110101010010110100110000010100012 1.043110…2·2-1075 exp(-1011101000.01110000101010001000110110101110001110001002 1.142011…2·2-1075 exp(-1011101000.01110000101010001000110110101110001110000112 1.044110…2·2-1074
Интервал/точка -(1+2-52)·2-53
1+2-52
Использование различных режимов округления, однако, «размазывает» эти границы на целые интервалы. Чтобы вычислить эти интервалы, определим значения, экспоненты которых при округлении к ближайшему будут округляться вверх и давать 2-1074, а затем такие, экспоненты которых при этом будут округляться вниз и давать тот же результат. Имеем
Итоговые требования к значениям экспоненты в окрестности 0 для всех режимов округления суммированы в Таблице 1. к ближайшему
1
Ближайшие к ним представимые числа — -744.440071921381218089663889259099960327148437510 = -1011101000.01110000101010001000110110101110001110000112 и -708.39641853226407874899450689554214477539062510 = -1011000100.01100101011110111010111101010111100110100102.
exp(-1.02·2-54) = 1.1111111111111111111111111111111111111111111111111111 1055111…2·2-1.
к0
1
o Асимптоты. Экспонента стремится к 0 при x -∞, поэтому нужно определить значения аргумента, при которых значение экспоненты становится неотличимым от 0, а также важно знать, когда оно становится денормализованным. Минимальное положительное представимое число равно 2-1074, а минимальное положительное нормализованное число равно 2-1022. Их натуральные логарифмы равны, соответственно, -744.440071921381262314107298446...10 и -708.396418532264106224411228130…10.
exp(-1.02·2-53) = 1.1111111111111111111111111111111111111111111111111111 054111…2·2-1;
к -∞
1
Таблица 1. Требования к поведению экспоненты в окрестности 0.
Аналогично, для чисел, меньших 0 exp(-1.00000000000000000000000000000000000000000000000000012·2-53) = 1.1111111111111111111111111111111111111111111111111110 152010…2·2-1;
Округление
от 2-1074до (253-1)·2-106
= x1 ) = = x2 ) = = x3 ) = = x4 ) =
Аналогично, для минимального положительного нормализованного числа имеем
93
94
значения экспоненты для всех таких чисел, кроме 0, трансцендентны согласно теореме Линдемана [91], следовательно, представимы лишь приблизительно. Если x или y равно 0, это свойство не добавляет ничего нового к условию exp(0) = 1.
exp(-1011000100.01100101011110111010111101010111100110100112 = x5) = 0.1111111111111111111111111111111111111111111001111011 1011…2·2-1022 exp(-1011000100.01100101011110111010111101010111100110100102 = x6) = 1.0000000000000000000000000000000000000000000001111011 1011…2·2-1022.
Корректное округление. Погрешность вычисления экспоненты согласно стандарту ISO/IEC 10967-2 [14] не должна превосходить 0.5-1.5 ulp. Остановимся на таких требованиях. В качестве дополнительной информации можно выдавать предупреждение специального вида каждый раз, когда фактическая ошибка превышает 0.5 ulp. Для вычисления правильно округленного значения экспоненты можно воспользоваться методом, описанным в [84] или готовыми библиотеками [79,81,82].
Тестовые значения. Проведенный выше анализ свойств экспоненциальной функции, а также выделение денормализованных и исключительных чисел разбивают всю числовую прямую на следующие точки и интервалы.
Результирующие требования к значениям экспоненты, близким к минимальному положительному представимому числу и минимальному положительному нормализованному числу, приведены в Таблице 2. Округление
к -∞
к0
к ближайшему
к +∞
0
0
0
2-1074
Интервал/точка x1 x
-1074
2-1074 2-1073
x3 x x2
0
0
2
x4
2-1074
2-1074
2-1074
x5 > x > x4 x5 x6
денормализованное 0.1430011110112•2-1022 45
1.0 11110112•2
-1022
0.1430011111002•2-1022
o -∞;
1.04511111002•2-1022
o [-1.11111111111111111111111111111111111111111111111111112·21023, -1011101001.00100010000110101010010110100110000010100102];
Таблица 2. Требования к поведению экспоненты при переходе ее значений от нуля к положительным и от денормализованных к нормализованным значениям.
o [-1011101001.00100010000110101010010110100110000010100012, -1011101000.01110000101010001000110110101110001110001002]; o [-1011101000.01110000101010001000110110101110001110000112, -1011000100.01100101011110111010111101010111100110100112];
Асимптотика exp(x) ~ 1+x с трудом поддается фиксации в виде четких требований по ее соблюдению (см. раздел 4.1), поэтому никаких связанных с ней ограничений не налагается.
o [-1011000100.01100101011110111010111101010111100110100102, -53 -1.00000000000000000000000000000000000000000000000000012·2 ];
Область значений. Область значений экспоненты ограничена снизу нулем. Однако, это ограничение для всех значений аргумента следует из требований к точности вычислений. Интервал, на котором значения экспоненты денормализованы, уже определен выше при рассмотрении асимптот.
o [-1.02·2-53, -54 -1.00000000000000000000000000000000000000000000000000012·2 ]; o [-1.02·2-54, -1.02·2-1022]; o [-0.11111111111111111111111111111111111111111111111111112·2-1022, -1074 -1.02·2 ];
Монотонность и сохранение знака. В связи со сказанным в предыдущем абзаце тот факт, что экспонента имеет только положительные значения, можно не проверять отдельно. Экспонента — возрастающая функция. Соответственно, ее реализация также должна возрастать.
o -0; o 0; o [1.02·2-1074, 0.11111111111111111111111111111111111111111111111111112·2-1022];
Симметрии и периодичность. Единственное важное свойство экспоненты — exp(x+y) = exp(x)·exp(y) — может выполняться лишь приблизительно для чисел с плавающей точкой, поскольку
o [1.02·2-1022, 1.11111111111111111111111111111111111111111111111111112·2-54]; o [1.02·2-53, 1.11111111111111111111111111111111111111111111111111112·2-53];
95
96
π/2+π·n = (2n+1)·π/2, значит π/2 m·2k/(2n+1). Таким образом, можно искать подходящие числа как числители наилучших рациональных приближений к π/2. Для этого разложим числа π·2k при k от 52 до -971 в непрерывные дроби и, обрывая их в каком-либо месте, вычислим соответствующие подходящие дроби. Эти дроби приближают исходные числа наилучшим образом среди всех рациональных чисел, имеющих меньшие или такие же знаменатели [92].
o [1.02·2-52, 1011000101.11001000010111111101111101000111001111011112]; o [1011000101.11001000010111111101111101000111001111100002, 1.11111111111111111111111111111111111111111111111111112·21023]; o +∞.
Выполнение соответствующих вычислений дает 920 дробей, которые однозначно определяют числа с плавающей точкой двойной точности, приближающие кратные π/2 с относительной погрешностью не больше, чем 2-57. Вот несколько первых таких чисел.
На полученных интервалах нужно строить тестовые значения в соответствии с методом, описанным в разделе 4.2 — деля интервал на n частей и выбирая точки, отстоящие от полученных (n+1) точки на интервале не более чем на k чисел с плавающей точкой.
29·π/2 = 101101.10001101100101111000100010111011100100011011101 113010…2
Кроме этого, к полученному множеству тестовых значений надо добавить нескольких представителей NaN и набор чисел, вычисление корректно округленного значения экспоненты для которых наиболее трудоемко. Приведем несколько таких примеров, взятых из [17].
14479·π/2 = 101100011010111.10001111010111010010111110111111001010 014110…2 29327·π/2 = 1011001111110010.1011111001101110101001101110001010011 115011…2
exp(-1.11101101001100011000111011111011011000100111111010102·2-27) = 1.1111111111111111111111111000010010110011100111000100 160000…2·2-1
И несколько последних.
exp(-1.01000000000000000000000000000000000000000000001100102·2-46) = 1.1111111111111111111111111111111111111111111101100000 085101…2·2-1
197618317288004252206364340334461523249938251852771804320737665131462 691696567271297312161164216527568595012444936741107384701949271514894 825117746798310262428988560159244225487630579194650628623442695547951 691080982624236830670934028824074039328312895391481654408301108215527 83846121469368255050296717288515·π/2 = 1.0110000110100011110110111000110010001101000100101001 01026111…2·21021
exp(-1.00000000000000000000000000000000000000000000000000012·2-51) = 1.1111111111111111111111111111111111111111111111111100 0101101…2·2-1 exp(1.11111111111111111111111111111111111111111111111111112·2-53) = 1.0000000000000000000000000000000000000000000000000000 1105010…2; это конец одного из выделенных интервалов; exp(1.11111111111111111111111111111111111111111111100000002·2-46) = 1.0000000000000000000000000000000000000000000001111111 184010…2;
258215239476700584226130900745995853560281748618927419956422039974202 286570698806549395630266453068250888197386507164887712080806671331638 295216484470333049129476424667965728976743334784463915624846487050650 888702720312158190710847644636354056948141468538860276324737125629625 98749697007058695124283117548543·π/2 = 1.1100111000010100001100101110101011000111000001000111 11020011…2·21021
exp(1.00011111111111111111111111111111111111111111101011112·2-45) = 1.0000000000000000000000000000000000000000000010001111 184000…2; exp(110.000011110101001011110011011110101110110011111101002) = 110101100.01010000101101000000100111001000101011101110 058100…2
4.3.2. Тангенс
Хотя относительная погрешность приближения кратного π/2 может достигать 2-1079, абсолютное расстояние до него для большинства таких чисел находится в пределах 10-15-10-17. Только 25 из этих чисел лежат от полюсов на расстоянии, не превосходящем 10-17, и только два — на расстоянии, не превосходящем 10-18. В Таблице 3 перечислены значения 25 нечетных кратных π/2, ближайших к числам с плавающей точкой и значения тангенса для соответствующих чисел с плавающей точкой.
Область определения и особые точки. Тангенс определен для всех действительных чисел, кроме чисел вида π/2+π·n для целого n. Эти исключительные точки являются полюсами, в которых тангенс имеет пределы +∞ справа и -∞ слева. Чтобы описать требования на поведение тангенса в окрестностях его полюсов, необходимо определить, насколько близко могут быть расположены числа с плавающей точкой к числам такого вида. Любое число с плавающей точкой, большее 1, может быть представлено как m·2k, где m — натуральное число, не превосходящее 253-1, а k — натуральное число, не превосходящее 971. Если такое число расположено очень близко к 97
98
15624927791716915161759873344092142005165621843671972171731789642962 257024455·π/2 = 1.1011001000011001011000110110010011010111010100001010 1258011…2·2253
29·π/2 = 101101.10001101100101111000100010111011100100011011101 113010…2 tg(101101.100011011001011110001000101110111001000110111102) = -1.0110011010111001111010111100010010000101000011000110 0110…2·260
tg(1.10110010000110010110001101100100110101110101000010112·2253) = -1.1101110100010110100010001100011010010110100110101100 13011…2·257
9206271·π/2 = 110111001010100011111000.10101011100101110101110100101 030111…2
71610307166660314043397602176728270128783849452911391884618409783207 463478073649776722681·π/2 = 1.1100010001011100110100010001000101010100110111111101 0300100…2·2295
tg(110111001010100011111000.101010111001011101011101001012) = 1.0000010101110101100001001100010000101001101100111010 0110…2·259
tg(1.11000100010111001101000100010001010101001101111111012·2295) = 1.1111110110111010010001101111010101010011101000001000 10310…2·257
138049179777104367775·π/2 = 1.0111100000101011011110100010000011011111011011010100 072101…2·267 tg(1.01111000001010110111101000100000110111110110110101002·267) = 1.0101101001100011110010000010101101101101101011100010 0100…2·257
79531021962858779827512141402968504124268502489089591753854287169082 2757973300060931533046037509210740152847560365069332549·π/2 = 1.1110001111001010100110110110110001100101010111001011 0412100…2·2408
1104381301317041933816362171·π/2 = 1.0110011010111101010101000010010011100101011001010101 095101…2·290
tg(1.11100011110010101001101101101100011001010101110010112·2408) = 1.1001111110011110010101010001001001100110110000111100 01300…2·256
tg(1.01100110101111010101010000100100111001010110010101012·290) = 1.0101101001011110001010111000100001101010001010101010 13010…2·257
43776696795279815301055904915945700987301503939761311288501927384592 34187554875937911988865352801115519693682101780670312164186360396953 23195218767·π/2 = 1.1011100010001100101110110100111000110010010101110101 1491011…2·2487
2276647626870351330792862584889027286951·π/2 = 1.0101000001001100101011000101000111110001111010101111 0137100…2·2131 tg(1.01010000010011001010110001010001111100011110101011112·2131) = 1.1110011111000111110100001111010000111111100000011101 10511…2·258
tg(1.10111000100011001011101101001110001100100101011101102·2487) = -1.1010001011000010110011001011110000011010011000110010 0101…2·256
9617272285741355388977328677436746192449293·π/2 = 1.0101101011010101101001100010110010110001110011001000 1147011…2·2143
11589330310381988626449129598171351352584091074116125961273394956149 19456683050991296076480489809564268564293673421768988424019510581475 74305293329168873163869152174973·π/2 = 1.1000101100101000011001110110110011011100110001011010 1560000…2·2555
tg(1.01011010110101011010011000101100101100011100110010012·2143) = -1.1100011110101011011100111000100111000010010011010111 0110…2·256
tg(1.10001011001010000110011101101100110111001100010110112·2555) = -1.0000101000010001011001101101100011001010011011100010 0101…2·257
3982124097056689019860931670115672371080901279195265295·π/2 = 1.0000010100111001101101001000110100010100110001010101 0186100…2·2182
40912440333305918481550440392603355393318151657874369033644916645778 80843430257120834658307295698261362420794202530508444695188784113250 59263543780254873744888889331055895843·π/2 = 1.0100110010010110110000010001000100110100110100110101 1583010…2·2577
tg(1.00000101001110011011010010001101000101001100010101012·2182) = 1.1010011111001011011111001011010000101000000010110000 03110…2·256 243709684824005521714504726240306266173066571317974432621719·π/2 = 1.1110011111100100010010100111100010101100000110001011 1203000…2·2197
tg(1.01001100100101101100000100010001001101001101001101102·2577) = -1.0110010101011100111110011110001000111100010011010111 03101…2·258
tg(1.11100111111001000100101001111000101011000001100011002·2197) = -1.0000100110111010011111100000011000011111001100000000 1001…2·258 10177187664528444025108553957230483888384395218254373185418290015777 4575619·π/2 = 1.0110100111101010101100001001100001010001011110011011 0251101…2·2246
65429133524241263178237370719527517029251219555784828550385052067786 68733143053622851340353340621053795171428918994903139424630388205704 7519616430579116455387138549345777195516511603705·π/2 = 1.1000001100000000100111100010111010011110001011101010 1619011…2·2614
tg(1.01101001111010101011000010011000010100010111100110112·2246) = 1.0111001001010110011111001011100100000100011110101000 13001…2·257
tg(1.10000011000000001001111000101110100111100010111010112·2614) = -1.1110000101100010000111111011000100110011011010100100 12011…2·257
99
100
59287932212270746098210389073106720676473902052684288108187468991207 27939955976870793398691724674236683942417720739021351535557283241575 82755209798363213599827165644437783394500726283264184124810624461674 9·π/2 = 1.1101101101000001111100111100101101110001110101111011 0686111…2·2680
37105172474923906782576160374346777164937755101941743463232913447611 26458820318029413443054532013548715976758198761455677008210344281793 85754829765785467791950462488227489950141733334740102736828752663860 04962920828212934138031374382514718179363737540556512625927642564615 90125145265·π/2 = 1.0100000100010111010101110011001110010111110101000001 1944000…2·2939
tg(1.11011011010000011111001111001011011100011101011110112·2680) = 1.0001000010111010011011110011111101000110011111010110 12010…2·258
tg(1.01000001000101110101011100110011100101111101010000102·2939) = -1.0001110001110010100101001010111011100111101110100100 01401…2·257
30636572260931163506332425320948767147146434821312896452845246587739 42999960543512781934069734059379292127565945745701814493759190791305 15095712133324352639295031679514263477241946753392456572308669671533 6885·π/2 = 1.1101111110101000110100011000111100101011001111101101 1693011…2·2689
22795463807341151743454041517545705884702298848407846038370271485088 07014891141753237567442204768461160038124562964557539085826195999224 02145326867889285827944189828230765840337018227362357217203013966072 70621803425566565987094025434998287231727147847848195901676458199728 364279528014629·π/2 = 1.1110000110011000011100010010001010110111111000000110 0955101…2·2951
tg(1.11011111101010001101000110001111001010110011111011102·2689) = -1.1011100111111000000110011010110110001011010111100111 10511…2·256
tg(1.11100001100110000111000100100010101101111110000001102·2951) = 1.0111000101110100000001111101001000000001111001100110 13011…2·256
94971440572386015595571166139691201230707691489980939180815478595748 85577578104219175989760881732308451273779191276410784862786933003137 63136525734828774533668148005758919334015282384829230967867833601055 56253079245416317281232917440947587·π/2 = 1.0110111010001101011101111000110010010100110101100110 0798100…2·2794
24982829712749183769184949361370722806939675044978450598655040276224 22069326569285540059089298879071869587152103490382783870770007392787 73724242538703521095769835440603290627555914001656747466652577114915 40415860945893559496946376666124169735622128694086982311970782271594 626856299313449263532321973·π/2 = 1.1110000000001001110001010011000101001000101111100001 0997100…2·2991
tg(1.01101110100011010111011110001100100101001101011001102·2794) = 1.1001111001110100010011011111100111010110100011111100 0101…2·256 33864178045159811206438920823311565991202393932998380352421215184285 37554064774221620930267583474709602068045686026362989271814411863708 49986972132271594662263430201169763297290792255889271083061603403854 1342154669787134871905353772776431251615694251273653·π/2 = 1.0110101011000101101100100110001011001010000111111110 1857011…2·2849
tg(1.11100000000010011100010100110001010010001011111000012·2991) = 1.1011100011001011111101111110110011111010111111111010 02111…2·258 19761831728800425220636434033446152324993825185277180432073766513146 26916965672712973121611642165275685950124449367411073847019492715148 94825117746798310262428988560159244225487630579194650628623442695547 95169108098262423683067093402882407403932831289539148165440830110821 552783846121469368255050296717288515·π/2 = 1.0110000110100011110110111000110010001101000100101001 01026111…2·21021
tg(1.01101010110001011011001001100010110010100001111111112·2849) = -1.1101100110111010100110100111100101110101011000110101 1010…2·260 88685758294706272204189579187820280422409877285489361868254170927334 81411325935922524369633276190848778076190586339609015909756150954578 31282700320668157612019880948068064298959801704400266492369256967459 7153720925148676684943087665815389546838546388224593403·π/2 = 1.1100111111100100100000100010100001011111100011101101 0866111…2·2860
tg(1.11100000000010011100010100110001010010001011111000012·21021) = 1.0001001010111100000100001001010101100001110011010100 10311…2·257 Таблица 3. Нечетные кратные π/2, ближайшие к числам с плавающей точкой и соответствующие значения тангенса.
tg(1.11001111111001001000001000101000010111111000111011012·2860) = 1.0001101000100100011010110110111110010010010001010110 12000…2·258
В 20-м ряду Таблицы 3 приведено число с плавающей точкой, ближайшее к нечетному кратному π/2. Это же число упоминается в статье [93], там оно вычислено с помощью программы, созданной W. Kahan и S. McDonald [94]. Из этого следует, что приведенное в 20-м ряду значение тангенса является максимальным по абсолютной величине. Таким образом, для всех чисел с
101
102
плавающей точкой двойной точности при всех режимах округления значения тангенса ограничены по абсолютной величине 2.13348538575370393610·1018. Более того, для всех чисел, не представленных в Таблице 3, значения тангенса должны по абсолютной величине быть меньше, чем минимальное значение тангенса в этой таблице, находящее в 23-м ряду и равное 1.0399184334316502410·1017.
o Другие точные значения. Кроме tg(0) = 0 заслуживают внимания соотношения tg(π/4+π·n) = 1 и tg(-π/4+π·n) = -1. Округление
к0
к -∞
к ближайшему
к +∞
Интервал/точка
Специальные значения, значения в 0, касательные и асимптоты.
x = -x4
o Специальные значения. Значение тангенса в точке -0 можно определить равным как 0, так и -0. Второй способ выглядит предпочтительнее, поскольку он более точно отражает асимптотику tg(x) ~ x при x ~ 0 и имеет аналогию в стандарте IEEE 754, определяющем sqrt(-0) = -0.
-(t4++)
-x2 x -x3
-((-x)++)
0 > x -x1
-((-x)++)
-t4 x
-((-x)++)
x=0
0
x1 x > 0 x3 x x2
Поскольку тангенс не имеет пределов в +∞ и -∞, его значением в этих точках можно считать только NaN. Таким образом, получаем следующее.
x = x4
tg(-0) = -0; tg(+∞) = NaN; tg(-∞) = NaN; tg(NaN) = NaN.
x
x
x x
x++ x++
t4
t4++
Таблица 4. Требования к поведению тангенса в окрестности 0.
Вычисление чисел, приближающих нечетные кратные π/4 с погрешностью, не превосходящей 2-52, дает 726 значений, которые могут рассматриваться как тестовые данные. Однако для многих из них тангенс не всегда должен быть равен 1 или -1. Например, для числа, ближайшего к π/4, значение тангенса равно 1.1111111111111111111111111111111111111111111111111111 01300…2·2-1, что дает 1 только при режиме округления к +∞.
o Окрестность 0, асимптотика. Известно, что tg(0) = 0. Кроме того, tg(x) ~ x при x ~ 0. Определим окрестность 0, в которой значение тангенса должно совпадать со значением его аргумента. Имеем tg(1.00100101000010111111111000011011000010000010111101002·2-26 = x1) = 1.0010010100001011111111100001101100001000001011110100 015001…2·2-26 = t1;
Таких чисел с плавающей точкой, для которых 1 является ближайшим числом к значению их тангенса 199. В Таблице 5 перечислены только 3 из них, которые отстоят от кратных π/4 не более чем на 10-19.
tg(1.00100101000010111111111000011011000010000010111101012·2-26 = x2) = 1.0010010100001011111111100001101100001000001011110101 105210…2·2-26 = t2;
29·π/4 = 10110.110001101100101111000100010111011100100011011101 113010…2
tg(1.01110001001101110100010010010001001000111110111101012·2-26 = x3) = 1.0111000100110111010001001001000100100011111011110101 150011…2·2-26 = t3;
tg(10110.1100011011001011110001000101110111001000110111102) = 1.0000000000000000000000000000000000000000000000000000 08101…2
tg(1.01110001001101110100010010010001001000111110111101102·2-26 = x4) = 1.0111000100110111010001001001000100100011111011110111 055100…2·2-26 = t4.
9206271·π/4 = 1.1011100101010001111100010101011100101110101110100101 030111…2·222 tg(1.10111001010100011111000101010111001011101011101001012·222) = -1.0000000000000000000000000000000000000000000000000000 07111…2
Следующие из этих соотношений требования к значениям тангенса в окрестности 0 для всех режимов округления сформулированы в Таблице 4. В этой таблице выражение x++ для числа с плавающей точкой x обозначает непосредственно следующее за x число с плавающей точкой. Анализ других нулей тангенса, π·n при n 0, см. ниже, в пункте о монотонности и сохранении знака.
103
104
33864178045159811206438920823311565991202393932998380352421215184 28537554064774221620930267583474709602068045686026362989271814411 86370849986972132271594662263430201169763297290792255889271083061 6034038541342154669787134871905353772776431251615694251273653·π/4 = 1.0110101011000101101100100110001011001010000111111110 1857011…2·2848
29·π = 1011011.0001101100101111000100010111011100100011011101 113010…2 tg(1011011.00011011001011110001000101110111001000110111102) = 1.0110110101100001101101011000110010011001110001000010 14000…2·2-60 9206271·π = 1101110010101000111110001.0101011100101110101110100101 030111…2
848
tg(1.01101010110001011011001001100010110010100001111111112·2 ) = 1.0000000000000000000000000000000000000000000000000000 08100…2
tg(1101110010101000111110001.01010111001011101011101001012) = -1.1111010101001111010100100010011110100100111010000011 15011…2·2-59 2276647626870351330792862584889027286951·π = 1.0101000001001100101011000101000111110001111010101111 0137100…2·2132
Таблица 5. Кратные π/4, ближайшие к числам с плавающей точкой и соответствующие значения тангенса.
tg(1.01010000010011001010110001010001111100011110101011112·2132) = -1.0000110010110110000001001101001101001111001101000001 02100…2·2-58
o Асимптоты. У тангенса нет горизонтальных асимптот. Асимптотика в 0 рассмотрена выше.
243709684824005521714504726240306266173066571317974432621719·π = 1.1110011111100100010010100111100010101100000110001011 1203000…2·2198
Область значений. Область значений тангенса не имеет математических границ. Для чисел с плавающей точкой, как мы выяснили выше, тангенс не должен превосходить по абсолютной величине 2.13348538575370393610·1018. Однако это требование следует из ограничений на точность вычислений. Из Таблицы 4 также следует, что значение тангенса денормализовано в точности тогда, когда денормализован его аргумент.
tg(1.11100111111001000100101001111000101011000001100011002·2198) = 1.1110110101000001010111110101010011001000110010111000 0101…2·2-58
Монотонность и сохранение знака. Тангенс возрастает на каждом отрезке от π/2+π·n до π/2+π·(n+1). Монотонность имеет смысл проверять только для тех значений аргументов, которые попадают в один такой отрезок. Тангенс отрицателен на отрезках от π/2·(2n-1) до π·n, равен 0 в точках π·n и положителен на отрезках от π·n до π/2·(2n+1). Для проверки аккуратной смены знака нужно определить числа с плавающей точкой, ближайшие к кратным π.
15624927791716915161759873344092142005165621843671972171731789642962 257024455·π = 1.1011001000011001011000110110010011010111010100001010 1258011…2·2254
10177187664528444025108553957230483888384395218254373185418290015777 4575619·π = 1.0110100111101010101100001001100001010001011110011011 0251101…2·2247 tg(1.01101001111010101011000010011000010100010111100110112·2247) = -1.0110000111101100111011001001110001010111011111111101 02111…2·2-57
tg(1.10110010000110010110001101100100110101110101000010112·2254) = 1.0001001010111011101111100000011111110010110111100001 02110…2·2-57 71610307166660314043397602176728270128783849452911391884618409783207 463478073649776722681·π = 1.1100010001011100110100010001000101010100110111111101 0300100…2·2296 tg(1.11000100010111001101000100010001010101001101111111012·2296) = -1.0000000100100100001010000111011011010111110000011010 10410…2·2-57
Можно использовать те же 920 дробей, которые были найдены нами ранее для приближения π/2 — дроби для π отличаются только удвоенными числителями, а соответствующие числа с плавающей точкой имеют ту же мантиссу и на единицу большую экспоненту.
40912440333305918481550440392603355393318151657874369033644916645778 80843430257120834658307295698261362420794202530508444695188784113250 59263543780254873744888889331055895843·π = 1.0100110010010110110000010001000100110100110100110101 1583010…2·2578
Ближайшие к нулям тангенса числа с плавающей точкой выглядят несколько иначе, чем ближайшие к полюсам, хотя соответствуют почти тем же числам, умноженным на π. Все такие числа, расстояние от которых до кратных π не превосходит 10-17, а также значения тангенса для них представлены в Таблице 6.
105
tg(1.01001100100101101100000100010001001101001101001101102·2578) = 1.0110111011000110011110111100111101110111010100100010 0100…2·2-58
106
числу, что и ближайшее к числу с плавающей точкой нечетное кратное π/2.
65429133524241263178237370719527517029251219555784828550385052067786 68733143053622851340353340621053795171428918994903139424630388205704 7519616430579116455387138549345777195516511603705·π = 1.1000001100000000100111100010111010011110001011101010 1619011…2·2615
Симметрии и периодичность. Тангенс — нечетная функция, tg(-x) = -tg(x). Это свойство должно учитываться при построении всех тестов с отрицательными значениями аргумента. Периодичность с трансцендентным периодом π, как и симметрии вида tg(π/2-x) = 1/tg(x), выполняются на числах с плавающей точкой лишь приблизительно и, поэтому не могут быть использованы для формулировки строгих требований.
Корректное округление. Погрешность вычисления тангенса согласно стандарту ISO/IEC 10967-2 [14] не должна превосходить 0.5-2.0 ulp. В качестве дополнительной информации можно выдавать предупреждение специального вида каждый раз, когда фактическая ошибка превышает 0.5 ulp. Для вычисления правильно округленного значения тангенса можно использовать известные библиотеки вычислений с заданной точностью [79,81,82].
Тестовые значения. Идеальный набор интервалов для дальнейшего построения тестовых данных для тестирования реализаций тангенса мог бы состоять из всех его периодов, в которые попадает хотя бы два числа с плавающей точкой. Однако, таких интервалов слишком много — около 5.7·1015. Поэтому необходимо выбрать только некоторые из таких периодов, руководствуясь каким-то правилом.
tg(1.10000011000000001001111000101110100111100010111010112·2615) = 1.0001000001001000001100000100000000001001000100101001 1011…2·2-57 59287932212270746098210389073106720676473902052684288108187468991207 27939955976870793398691724674236683942417720739021351535557283241575 82755209798363213599827165644437783394500726283264184124810624461674 9·π = 1.1101101101000001111100111100101101110001110101111011 0686111…2·2681 tg(1.11011011010000011111001111001011011100011101011110112·2681) = -1.1110000010011000011110001101001000101110001010011101 02101…2·2-58 33864178045159811206438920823311565991202393932998380352421215184285 37554064774221620930267583474709602068045686026362989271814411863708 49986972132271594662263430201169763297290792255889271083061603403854 1342154669787134871905353772776431251615694251273653·π = 1.0110101011000101101100100110001011001010000111111110 1857011…2·2850 tg(1.01101010110001011011001001100010110010100001111111112·2850) = 1.0001010010101110011100101110011010111010001000101110 14010…2·2-60 88685758294706272204189579187820280422409877285489361868254170927334 81411325935922524369633276190848778076190586339609015909756150954578 31282700320668157612019880948068064298959801704400266492369256967459 7153720925148676684943087665815389546838546388224593403·π = 1.1100111111100100100000100010100001011111100011101101 0866111…2·2861
Предлагается в качестве основы для тестов выбрать периоды тангенса, содержащие хотя бы одну из упоминавшихся выше точек, близких к кратным π, π/2 и π/4 (включая 0). Таких точек меньше, чем 3000, поэтому получающийся набор тестовых данных будет одновременно достаточно представительным и обозримым. Кроме того, к ним можно добавить несколько периодов, близких к содержащему 0, например, от -29π до 29π (раз уж число 29 дает одно из самых близких кратных π к числу с плавающей точкой). Для каждого из выбранных интервалов нужно вычислить его левый и правый концы — точки, ближайшие к нечетным кратным π/2 на этом интервале, его точку, ближайшую к кратному π, а также две точки на этом интервале, ближайшие к нечетным кратным π/4. Эти точки разбивают каждый такой период на четыре под-интервала. Все их занесем в исходное множество.
861
tg(1.11001111111001001000001000101000010111111000111011012·2 ) = -1.1101000010001111010110011100001100101011111110100010 12010 …2·2-58 24982829712749183769184949361370722806939675044978450598655040276224 22069326569285540059089298879071869587152103490382783870770007392787 73724242538703521095769835440603290627555914001656747466652577114915 40415860945893559496946376666124169735622128694086982311970782271594 626856299313449263532321973·π = 1.1110000000001001110001010011000101001000101111100001 0997100…2·2992 tg(1.11100000000010011100010100110001010010001011111000012·2992) = -1.0010100101011010001110110000101001100100101100011101 0100…2·2-58 Таблица 6. Кратные π, ближайшие к числам с плавающей точкой и соответствующие значения тангенса.
Кроме того, в исходное множество попадут точки x1, -x1, x2, -x2, x3, -x3, x4, -x4, вычисленные при рассмотрении асимптотики тангенса в 0, сам 0, -0, +∞ и -∞, а также минимальное и максимальное положительные денормализованные числа и противоположные им.
Отметим, что ближайшее к числу с плавающей точкой кратное π число, находящееся в 11-м ряду Таблицы 6, соответствует точно такому же целому
107
108
Полученные таким образом интервалы можно разбить далее, пользуясь техникой, описанной в разделе 4.2. Кроме этого, к тестовым значениям надо добавить нескольких представителей NaN и набор чисел, вычисление корректно округленного значения тангенса для которых наиболее трудоемко. Вот примеры таких чисел из [17]. tg(1.11011111111111111111111111111111111111111111000111112·2-22) = 1.1110000000000000000000000000000000000000000101010001 017801…2·2-22
описываемого одним набором условий, поведения тестируемой функции. Кроме того, используются полученные в работах [17,72] числа с плавающей точкой, для которых определение корректно округленного значения данной функции требует наиболее высокой точности вычислений. Разработанные в рамках данного исследования методики покрывают все основные проблемы разработки тестов для реализаций математических функций и позволяют перевести решение отдельных задач в этой области в практическую плоскость.
Литература [1]
tg(1.01100111111111111111111111111111111110100001000101002·2-18) = 1.0110100000000000000000000000000000001000111001100001 158010…2·2-18 tg(1.01010000010010000110101100101111100001110000000101002·2-5) = 1.0101000001111000110011101011111111111001110001110010 105710…2·2-5 tg(1.01000110101011000011011100100010010000110101001101102·2-1) = 1.0111101110100100100111110111001110011000001010011110 155001…2·2-1.
5. Заключение В данной работе проведен анализ проблем проверки корректности вычисления математических функций, реализованных на основе чисел с плавающей точкой. Этот анализ, а также обзор литературы по данному вопросу дают возможность утверждать, что задача построения систематических тестов для библиотек математических функций достаточно актуальна. Несмотря на наличие серьезных исследований этого вопроса, например, в работах [16,17,72,73], ведущихся в рамках проекта Arenaire [12], работ, в которых для построения тестов таких функций использовались бы их формальные спецификации, нет. В рамках данной работы был предложен метод разработки тестов для реализаций математических функций на основе формальных спецификаций. Этот метод основывается на технологии UniTesK разработки тестов для ПО общего назначения и двух отдельных методиках: методике формирования требований к реализации конкретной математической функции и методике выбора тестовых данных для тестирования этой реализации с учетом особенностей представления чисел с плавающей точкой и сформулированных требований. Методика формирования требований построена на основе анализа особенностей и специфических элементов поведения самой математической функции с учетом использования чисел с плавающей точкой для представления ее аргументов и значений. Методика выбора тестовых данных основана на разбиении чисел с плавающей точкой на ряд интервалов, границами которых являются числа с плавающей точкой, специфические с точки зрения их представления или же с точки зрения сформулированных требований к поведению функции. Можно сказать, что большинство таких интервалов являются интервалами «однородного», т.е. 109
I. Bourdonov, A. Kossatchev, V. Kuliamin, and A. Petrenko. UniTesK Test Suite Architecture. In Proc. of FME 2002. LNCS 2391, pp. 77–88, Springer-Verlag, 2002. [2] В. В. Кулямин, А. К. Петренко, А. С. Косачев, И. Б. Бурдонов. Подход UniTesK к разработке тестов. Программирование, 29(6):25–43, 2003. [3] V. Kuliamin, A. Petrenko, N. Pakoulin. Practical Approach to Specification and Conformance Testing of Distributed Network Applications. In M. Malek, E. Nett, N. Suri, eds. Service Availability. LNCS 3694, pp. 68–83, Springer-Verlag, 2005. [4] IEEE 754-1985. IEEE Standard for Binary Floating-Point Arithmetic. NY: IEEE, 1985. [5] IEC 60559:1989. Binary Floating-Point Arithmetic for Microprocessor Systems. Geneve: ISO, 1989. [6] IEEE 854-1987. IEEE Standard for Radix-Independent Floating-Point Arithmetic. NY: IEEE, 1987. [7] D. Goldberg. What Every Computer Scientist Should Know about Floating-Point Arithmetic. ACM Computing Surveys, 23(1):5-48, 1991. [8] ISO/IEC 9899:1999. Programming Languages — C. Geneve: ISO, 1999. [9] IEEE 1003.1-2004. Information Technology — Portable Operating System Interface (POSIX). NY: IEEE, 2004. [10] G. Hanrot, V. Lefevre, J.-M. Muller, N. Revol, and P. Zimmermann. Some Notes for a Proposal for Elementary Function Implementation in Floating-Point Arithmetic. Proc. of Workshop IEEE 754R and Arithmetic Standardization, in ARITH-15, June 2001. [11] D. Defour, G. Hanrot, V. Lefevre, J.-M. Muller, N. Revol, and P. Zimmermann. Proposal for a standardization of mathematical function implementation in floating-point arithmetic. Numerical Algorithms, 37(1–4):367–375, December 2004. [12] http://www.inria.fr/recherche/equipes/arenaire.en.html [13] ISO/IEC 10967-1:1994. Information Technology — Language Independent Arithmetic — Part 1: Integer and Floating Point Arithmetic. Geneve: ISO, 1994. [14] ISO/IEC 10967-2:2002. Information Technology — Language Independent Arithmetic — Part 2: Elementary Numerical Functions. Geneve: ISO, 2002. [15] ISO/IEC 10967-3. Information Technology — Language Independent Arithmetic — Part 3: Complex Integer and Floating Arithmetic and Complex Elementary Numerical Functions. Draft. Geneve: ISO, 2002. [16] V. Lefevre, J.-M. Muller, and A. Tisserand. Toward Correctly Rounded Transcendentals. IEEE Transactions on Computers, 47(11):1235–1243,November 1998. [17] V. Lefevre, J.-M. Muller. Worst Cases for Correct Rounding of the Elementary Functions in Double Precision. Proc. of 15-th IEEE Symposium on Computer Arithmetic, Vail, Colorado, USA, June 2001. [18] M. Abramowitz and I. A. Stegun, eds. Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical Tables. Dover Publications, 1965.
110
[19] J.-M. Muller. Elementary Functions: Algorithms and Implementation. Second edition. Birkhauser, Boston, 2006. [20] http://www.zuser.org/z/ [21] G. Barrett. Formal Methods Applied to a Floating-Point Number System. IEEE Transactions on Software Engineering, 15(5):611–621, May 1989. [22] http://www.csr.ncl.ac.uk/vdm/ [23] B. A. Wichmann. Towards a Formal Specification of Floating Point. The Computer Journal, 32:432–436, 1989. [24] http://www.cs.cornell.edu/Info/Projects/NuPrl/nuprl.html [25] http://pvs.csl.sri.com/ [26] http://www.cl.cam.ac.uk/Research/HVG/HOL/ [27] http://www.cs.utexas.edu/users/moore/acl2/acl2-doc.html [28] J. O’Leary, M. Leeser, J. Hickey, and M. Aagaard. Non-restoring Integer Square Root: A Case Study in Design by Principled Optimization. In T. Kropf, R. Kumar, eds. Proc. of the 2-nd International Conference on Theorem Provers in Circuit Design (TPCD’94): Theory, Practice, and Experience. LNCS 901, pp. 52–71, Springer-Verlag, 1994. [29] P. S. Miner. Defining the IEEE-854 Floating-Point Standard in PVS. Technical report TM-110167, NASA Langley Research Center, 1995. [30] V. A. Carreno and P. S. Miner. Specification of the IEEE-854 Floating-Point Standard in HOL and PVS. Proc. of 10-th International Workshop on Higher Order Logic Theorem Proving and its Applications, Aspen Grove, Utah, USA, September 1995. [31] V. A. Carreno. Interpretation of IEEE-854 Floating-Point Standard and Definition in the HOL System. Technical report TM-110189, NASA Langley Research Center, 1995. [32] J. Moore, T. Lynch, and M. Kaufmann. A Mechanically Checked Proof of the Correctness of the Kernel of the AMD5K86 Floating-Point Division Algorithm. IEEE Transactions on Computers, 47(9):913–926, September 1998. [33] J. Harrison. Theorem Proving with the Real Numbers. Technical report UCAM-CLTR-408, University of Cambridge Computer Laboratory. UK, November 1996. [34] D. Verkest, L. Claesen, and H. De Man. A Proof on the Nonrestoring Division Algorithm and its Implementation on an ALU. Formal Methods in System Design, vol. 4, 1994. [35] M. Cornea-Hasegan. Proving the IEEE Correctness of Iterative Floating-Point Square Root, Divide, and Remainder Algorithms. Intel Technology Journal, 1998. [36] M. Cornea-Hasegan. IA-64 Floating Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic. Intel Technology Journal, Q4, 1999. [37] M. D. Aagaard and C.-J. H. Seger. The Formal Verification of a Pipelined Doubleprecision IEEE Floating-Point Multiplier. Proc. ICCAD, IEEE, Nov. 1995, pp. 7–10. [38] P. S. Miner, and J. F. Leathrum. Verification of IEEE Compliant Subtractive Division Algorithms. Proc. FMCAD'96, November 1996. [39] E. M. Clarke, S. M. German, and X. Zhao. Verifying the SRT Division Algorithm Using Theorem Proving Techniques. Proc. CAV'96, LNCS 1102, Springer-Verlag, 1996. [40] H. Ruess, N. Shankar, and M. K. Srivas. Modular Verification of SRT Division. Proc. CAV'96, LNCS 1102, Springer-Verlag, 1996. [41] R. Kaivola and N. Narasimhan. Formal Verification of the Pentium 4 Floating-Point Multiplier. Proc. of Design, Automation and Test in Europe Conference and Exposition (DATE). IEEE, 2002. [42] Y.-A. Chen, E. M. Clarke, P.-H. Ho, Y. Hoskote, T. Kam, M. Khaira, J. W. O'Leary, and X. Zhao. Verification of All Circuits in a Floating-Point Unit Using Word-level Model Checking. In Formal Methods in Computer-Aided Design, LNCS 1166, pp. 19-33, Springer-Verlag, 1996. 111
[43] J. R. Harrison. Verifying the Accuracy of Polynomial Approximations in HOL. TPHOLs'97, August 1997. [44] D. M. Russinoff. A Mechanically Checked Proof of IEEE Compliance of the Floating Point Multiplication, Division and Square Root Algorithms of the AMD-K7 Processor. LMS Journal of Computation and Mathematics, 1:148–200, 1998. [45] D. M. Russinoff. A Mechanically Checked Proof of IEEE Compliance of the AMD K5 Floating-Point Square Root Microcode. Formal Methods in System Design, 14 (1), January 1999. [46] J. O'Leary, X. Zhao, R. Gerth, and C. H. Seger. Formally Verifying IEEE Compliance of Floating-Point Hardware. Intel Technology Journal, 1999. [47] M. D. Aagaard, R. B. Jones, and R. Kaivola. Formal Verification of Iterative Algorithms in Microprocessors. Proc. DAC 2000. ACM, 2000. [48] J. Sawada. Formal Verification of Divide and Square Root Algorithms Using Series Calculations. Proc. of ACL2 Workshop, Grenoble, France, April 2002. [49] J. R. Harrison. Floating-Point Verification in HOL Light: the Exponential Function. Technical Report UCAM-CL-TR-428, University of Cambridge Computer Laboratory. UK, June 1997. [50] A. T. Abdel-Hamid. A Hierarchical Verification of the IEEE-754 Table-driven Floating-point Exponential Function Using HOL. Master's thesis, Dept. Electrical and Computer Engineering, Concordia University, Montreal, Quebec, Canada, 2001. [51] J. Harrison. Formal Verification of Floating Point Trigonometric Functions. Proc. of Formal Methods in Computer-Aided Design, FMCAD 2000, LNCS 1954, pp. 217-233, Springer-Verlag, 2000. [52] J. Harrison. Floating Point Verification in HOL. E. T. Schubert, P. J. Windley, and J. Alves-Foss, eds. Proc. of 8-th International Workshop on Higher Logic Theorem Proving and its Applications, Aspen Grove, UT, USA, September 1995. LNCS 971, pp. 186–199, Springer-Verlag, 1995. [53] F. de Dinechin, C. Lauter, and G. Melquiond. Assisted Verification of Elementary Functions. INRIA Research Report RR-5683, September 2005. [54] C. Jacob, C. Berg. Formal Verification of the VAMP Floating Point Unit. In Formal Methods in System Design, 26, pp. 227-266, Springer, 2005. [55] http://support.intel.com/support/processors/flag/tech.htm, Discussion of Flag Erratum, 2002. [56] http://www.math.utah.edu/~beebe/software/ieee/ [57] http://people.redhat.com/drepper/libm/ [58] W. Kahan. What Can You Learn about Floating-Point Arithmetic in One Hour? http://http.cs.berkeley.edu/~wkahan/ieee754status, 1996. [59] N. L. Schryer. A Test of Computer’s Floating-Point Arithmetic Unit. Computer Science Technical Report 89, AT&T Bell Labs, 1981. [60] B. Verdonk, A. Cuyt, and D. Verschaeren. A Precision- and Range-Independent Tool for Testing Floating-Point Arithmetic I: Basic Operations, Square Root and Remainder. ACM TOMS 27(1):92–118, 2001. [61] B. Verdonk, A. Cuyt, and D. Verschaeren. A Precision- and Range-Independent Tool for Testing Floating-Point Arithmetic II: Conversions. ACM TOMS 27(1):119–140, 2001. [62] http://www.cant.ua.ac.be/ieeecc754.html [63] R. Karpinski. PARANOIA: A Floating-Point Benchmark. Byte Magazine 10, 2 (Feb.), pp. 223–235, 1985. [64] http://www.netlib.org/paranoia/
112
[65] A. Ziv, M. Aharoni, and S. Asaf. Solving Range Constraints for Binary Floating-Point Instructions. Proc. of 16-th IEEE Symposium on Computer Arithmetic (ARITH-16’03), pp. 158–163, 2003. [66] M. Aharoni, S. Asaf, L. Fournier, A. Koifman, and R. Nagel. FPgen — A Test Generation Framework for Datapath Floating-Point Verification. Proc. IEEE International High Level Design Validation and Test Workshop (HLDVT’03), pp. 17–22, 2003. [67] http://www.netlib.org/fp/ucbtest.tgz [68] http://www.math.utah.edu/pub/elefunt/ [69] W. Cody and W. Waite. Software Manual for the Elementary Functions. PrenticeHall, Englewood Cliffs, NJ, 1980. [70] Z. A. Liu. Berkeley Elementary Function Test Suite. M.S. thesis, Computer Science Division, Dept. of Electrical Engineering and Computer Science, University of California at Berkeley, December 1987. [71] P.-T. P. Tang. Accurate and Efficient Testing of the Exponential and Logarithm Functions. ACM Transactions on Mathematical Software, 16(3):185–200, September 1990. [72] D. Stehle, V. Lefevre, P. Zimmermann. Searching Worst Cases of a One-Variable Function Using Lattice Reduction. IEEE Transactions on Computers, 54(3):340–346, March 2005. [73] http://www.loria.fr/~zimmerma/mpcheck/ [74] http://www.maplesoft.com/ [75] http://www.wolfram.com/products/mathematica/index.html [76] http://www.mathworks.com/products/matlab/ [77] A. Ziv. Fast Evaluation of Elementary Mathematical Functions with Correctly Rounded Last Bit. ACM Transactions on Mathematical Software, 17(3):410–423, September 1991. [78] IBM Accurate Portable MathLib http://rpmfind.net/linux/rpm2html/search.php?query=libultim.so.2 [79] http://www.mpfr.org/ [80] http://www.sun.com/download/products.xml?id=41797765 [81] http://www.ens-lyon.fr/LIP/Arenaire/Ware/SCSLib/ [82] http://lipforge.ens-lyon.fr/projects/crlibm/ [83] F. de Dinechin, A. Ershov, and N. Gast. Towards the post-ultimate libm. Proc. of 17-th Symposium on Computer Arithmetic. IEEE Computer Society Press, June 2005. [84] D. Defour, F. de Dinechin, J.-M. Muller. Correctly Rounded Exponential Function in Double Precision Arithmetic. INRIA Research report RR-2001-26, July 2001. [85] F. de Dinechin, C. Lauter, J.-M. Muller. Fast and Correctly Rounded Logarithms in Double-Precision. INRIA Research report RR-2005-37, September 2005. [86] S. Chevillard, N. Revol. Computation of the Error Functions erf and erfc in Arbitrary Precision with Correct Rounding. Proc. of 17-th IMACS Conf. on Scientific Computation, Applied Math. and Simulation, Paris, France, July 2005. [87] W. Kramer. Multiple-Precision Computations with Result Verification. In E. Adams, U. Kulisch, eds. Scientific Computing with Automatic Result Verification, pp. 325–356, Academic Press, 1993. [88] M. J. Schulte, E. E. Swartzlander. Software and Hardware Techniques for Accurate, Self-Validating Arithmetic. Applications of Interval Computations, pp. 381–404, 1996. [89] N. Revol, F. Rouillier. Motivations for an Aarbitrary Precision Interval Arithmetic and the MPFI Library. Reliable Computing, 11(4):275–290, 2005. [90] MPFI Library http://perso.ens-lyon.fr/nathalie.revol/mpfi_toc.html [91] F. Lindemann. Über die Zahl π. Mathematische Annalen, vol. 20, pp. 213–225, 1882. 113
[92] А. Я. Хинчин. Цепные дроби. М: Наука, 1978. [93] K. C. Ng. Arguments Reduction for Huge Arguments: Good to the Last Bit. 1992. Доступна как http://www.validlab.com/arg.pdf. [94] W. Kahan. Minimizing q*m − n. 1983. Неопубликованные заметки, доступны по http://http.cs.berkeley.edu/~wkahan/testpi/nearpi.c.
114
Разработка модельной реализации функций Бесселя из стандарта LSB
2. Функции Бесселя в стандарте LSB math.bessel math.bessel math.bessel math.bessel math.bessel math.bessel math.bessel
А. В. Пономаренко Московский физико-технический институт (государственный университет), Институтский переулок, 9, г. Долгопрудный, Московская область, Россия E-mail:
[email protected]
Аннотация В работе рассматривается модельная реализация функций Бесселя, входящих в состав интерфейсов, описанных в стандарте Linux Standard Base (LSB) [1]. Функции Бесселя играют важную роль в решении задач математической физики и широко применяются в электротехнике и радиотехнике. Цель работы состоит в создании реализации функций Бесселя, позволяющей проверить все свойства данных функций: тщательно проанализировать точность возвращаемых значений, проконтролировать значения функций в особых точках и в их окрестностях, их поведение на участках монотонности и асимптотику. Существует большое число методов вычисления функций Бесселя, которые позволяют получать значения функций в различных областях значений аргументов. Однако ни один метод не даёт нужной для тестирования точности вычисления на всей области определения. В модельной реализации применяется комбинация из наиболее эффективных методов, которые строго проанализированы на точность и документированы. Разработанная библиотека производит вычисление указанных функций с помощью модельного типа чисел с плавающей точкой, задаваемых с произвольной точностью.
j0 j1 jn y0 y1 yn
Таблица 1. Список функций Бесселя, описанных в рамках LSB [1].
3. Основные понятия Уравнение вида x2
Работа состоит из следующих частей. Сначала приведены основные свойства функций для ознакомления с ними. Затем приведён набор методов вычислений и исследование их точности. После этого проведён анализ ошибок арифметических операций, выполняемых при вычислениях по этим методам. Далее следует набор требований к реализациям функций, включающий в себя также анализ поведения в особых точках.
115
1k
2 k
x ! ( 1 ) k Г k 2 k 0
J ( x )
называют функцией Бесселя первого рода индекса . Здесь Г (a ) x a 1 e x dx — 0
гамма-функция (для целых неотрицательных n Г ( n ) ( n 1)! ). Решение вида Y ( x )
В работе рассматриваются наиболее эффективные из известных методов вычислений функций Бесселя первого и второго рода, J n (x) и Yn (x) соответственно. Тщательно проанализирована точность самих методов, а также точность выполняемых в ходе вычислений арифметических операций. Также в работе исследованы свойства функций Бесселя и их поведение в особых точках, составлены требования к реализациям этих функций.
d2y dy x x 2 2 y 0 dx 2 dx
называется уравнением Бесселя индекса . Решение уравнения Бесселя, имеющее вид
1. Введение Главная цель данной работы заключается в создании модельной реализации функций Бесселя, описанных в стандарте Linux Standard Base (LSB) [1].
функции Бесселя первого рода порядка 0 первого рода порядка 1 первого рода порядка n второго рода порядка 0 второго рода порядка 1 второго рода порядка n
J ( x ) cos( ) J ( x ) sin( )
называется функцией Бесселя второго рода индекса (а также функцией Неймана или функцией Вебера). В случае целых индексов n приведенное выражение для Y (x) понимается как предел при n . В работе исследуются функции Бесселя первого и второго рода для целых индексов
n.
4. Графики функций Графики функций J 0 ( x), J 1 ( x), J 2 ( x) изображены на Рис. 1, соответственно, сплошной, пунктирной и точечной линией.
116
Графики функций Y0 ( x), Y1 ( x), Y2 ( x) изображены на Рис. 2, соответственно, сплошной, пунктирной и точечной линией.
2. Y0 ( x )
2 x ln , x 0
2
n
3. Yn ( x ) 4. J n ( x ) ~ 5. Yn ( x ) ~
( n 1)! 2 , x 0, n 0 x 2
x 2
x
n cos x , x n 2 2 4 n sin x , x n 2 2 4
6. Вычисление факториала Во многих формулах для вычисления функций Бесселя присутствует факториал индекса n! . Индекс есть переменная типа int, которая может принимать максимальное значение 2147483647. Факториал этого числа огромен. В работе используется формула Стирлинга для представления n! в удобной для вычислений форме: n
n n! 2 n e S ( n ) , e
Рисунок 1. Графики функций J0(x), J1(x), J2(x).
S ( n) k 1
B2 k , 2k (2k 1)n 2 k 1
где B2 k есть числа Бернулли, определяемые по формулам B0 1 , m 1
(m 1) Bm Cmk 1 Bk . k 0
Например, в первом разложении Мейсселя (см. ниже), в формуле для вычисления en функции Бесселя первого рода есть множитель , который приводится с помощью n!n n формулы Стирлинга к удобному для вычислений виду e S (n ) . Рисунок 2. Графики функций Y0(x), Y1(x), Y2(x).
Первые 10 чисел Бернулли с четными номерами имеют следующие значения:
5. Асимптотики поведения функций Бесселя
1 B0 1, B2 , B4 6 691 , B14 B12 2730
Функции Бесселя имеют следующие асимптотики при различных условиях (см. [2,3]). n
1. J n ( x )
1 x , x 0 n! 2
117
118
1 1 1 5 , B6 , B8 , B10 , 30 42 30 66 7 3617 43867 174611 , B16 , B18 , B20 6 510 798 330
Поскольку, начиная с некоторого момента, числа Бернулли с номерами, делящимися на 4, отрицательны, а с номерами, не делящимися на 4, — положительны, ряд S (n) знакопеременный. Возьмём первые 10 его членов. Величина остатка ряда не превосходит первого отброшенного члена. Пренебрежение остатком ряда приводит к следующей относительной ошибке применения формулы Стирлинга.
B22 10 22 21 n 21 n 21
В алгоритмах вычислений формула Стирлинга будет применяться для n 100 . При таких условиях имеем следующую оценку относительной погрешности.
J n ( x)
1 n!2 n
x2 x2 x2 x2 1 1 1 ... ... 1 4 1 (n 1) 4 2 (n 2) 4 3 (n 3) 4 ( 1 ) ( 1 ) N n N
Величина остатка этого ряда меньше его N-го члена. Таким образом, ошибка метода есть величина N-го члена ряда. Из-за медленной сходимости и нарастания ошибки этот метод применяется для значений аргументов x n . Для значений аргумента x n он становится неустойчивым по отношению к промежуточным ошибкам, связанным с арифметическими действиями, выполняемыми в процессе вычисления. Вычисление функции отрицательного индекса сводится к вычислению функции положительного индекса с помощью формулы: J n ( x) (1) n J n ( x)
10 41 Этого вполне достаточно, так как везде далее будем добиваться гораздо большей относительной ошибки 10 20 .
7.1.1. Ошибка вычисления J n ( x)
7. Методы вычисления функций Поскольку различные методы вычисления функций Бесселя имеют разные характеристики сходимости в разных областях, для вычисления значений этих функций на всех значениях аргументов была выбрана следующая комбинация методов.
Представление рядом Тейлора в окрестности нуля ( x n ). Асимптотические разложения Ганкеля ( x n 2 ). Метод Стида ( x n ). Первое разложение Мейсселя ( x n, n 100 ). Второе разложение Мейсселя ( x n, n 100 ).
1k
x k!(n 1) (n 2) ... (n k )! 2
2k
2
x Ak 1 (n, x) 2 Ak (n, x) (k 1)(n k 1)
Везде далее целое число P означает выбираемое заранее количество хранимых значимых цифр в числах, с которыми оперируют вычислительные алгоритмы вычисления.
7.1.
Ak (n, x)
xn Ak (n, x) 2 n! k 0 n
Следовательно, Ak (n, x) при возрастании k сначала возрастают, а затем убывают. Исследуем ошибки для значений аргумента x n . R ( N , x, n) AN (n, x)
Представление J n (x) в виде степенного ряда
1 x N !(n 1) (n 2) ... (n N )! 2
2 N
есть относительная ошибка,
Согласно [2,3] J n (x) имеет следующее разложение в ряд Тейлора в окрестности 0.
1k
x k 0 k!( n k )! 2
J n ( x)
n 2k
так как
A (n, x) есть величина порядка единицы. k 0
k
R ( N , x, n) уменьшается при уменьшении x , рассмотрим максимум: N
n 4 R ( N , n , n) N !(n 1) (n 2) ... (n N )!
Для вычислений удобно считать (N-1)-ю частичную сумму ряда путём сведения к (N-1) произведениям для уменьшения промежуточных ошибок.
119
120
n RN 1 4 RN 1 N (n N ) 4 N
RN
7.2.
Представление Yn (x) в виде степенного ряда
Используем следующее представление функций Бесселя второго рода [2,3].
1 4 N N!
Yn ( x)
1 n 1 (n m 1)! x x J n ( x) (ln( ) ) 2 m 0 m! 2 2
Чтобы добиться относительной ошибки 10 20 нужно выбрать N , такое что
1 10 20 , 4 N N!
N 18
Попробуем расширить интервал, в котором можно достичь нужной относительной погрешности. Зафиксируем n 100 , x 100 , N 100 , для R получим
7.2.1. Ошибка вычисления Исследуем ошибки для значений аргумента x n . Для этого введём следующие обозначения.
200
50 7.89 10 36 200!
I1
50 202 R (n 100, x 100, N 101) 9.71 10 37 101 201!
A (n, x) 1.14 10 k 0
n m 1 m 1 k 1 k k 1 k
С помощью этого представления значение Yn (x) вычисляется для положительных индексов. Вычисление функции отрицательного индекса сводится к вычислению функции положительного индекса с помощью формулы: Yn ( x) (1) n Yn ( x) .
Далее заметим, что R (n, x, N ) убывает при дальнейшем увеличении N .
Величину суммы ряда оценим на компьютере:
n2m
В этой формуле при n 0 второй член (сумма по m от 0 до n-1) считается равным 0.
4 N N ! 10 20 ,
R (n 100, x 100, N 100)
(1) m x m 0 m!(m n)! 2 1
n2m
k
13
I2
I3
.
2
x J n ( x) (ln( ) ) , 2
(n m 1)! x m! m 0 2 1
n 1
(1) m x m0 m!(m n)! 2 1
Таким образом, величина относительной ошибки равна 7.89 10 36 / 1.14 10 13 6.92 10 23 .
n2 m
n 2 m
,
n m 1 m 1 , k 1 k k 1 k
Yn ( x) I1 I 2 I 3 .
Функция R (n, x, N ) убывает при уменьшении n , следовательно, имеем следующие интервалы, на которых достигается относительная ошибка 10 23 при выборе N 100 .
Проанализируем составляющие Yn (x) . Для I 3 выполнено I3
n 100, x n Помимо ошибки, присущей самому методу, возникает ещё дополнительная ошибка, связанная с неточностью выполнения операций сложения, вычитания, умножения, деления. Ее влияние может быть исключено путём увеличения количества значимых цифр P, хранимых при вычислениях (ошибка проанализирована в одном из последних разделов).
121
( 1) m x m0 m!( m n )! 2 1
n 2 m
n m 1 m 1 2 ( 1) m x m0 (m 1)!(m n )! 2 k 1 k k 1 k
n2 m
,
член имеет порядок равный порядку J n (x) . Таким образом, x I1 J n ( x) (ln( ) ) по порядку величины всегда больше I 2 . Если n 0 , x n то 2 I 2 по порядку величины больше I1 , так как I 2 содержит как отрицательные степени x , так и положительные (при n 2 ). т.е.
этот
2
122
Отсюда можно заключить, что при вычислении J n (x) и I 3 с некоторой относительной ошибкой , у Yn (x) относительная ошибка будет меньше . Число членов в I 3 для получения относительной ошибки 10 20 можно получить из неравенства, аналогичного выведенному при анализе J n (x) .
(1) m (n,2m 1) (2 x) 2 m1 m 0
I2
Рассмотрим, как ведут себя Am (n, x) . Am 1 (4n 2 (4m 3) 2 ) (4n 2 (4m 1) 2 ) 2 Am1 x 128 (2m 1) m
4 N ( N 1)! 10 20 N 20
Можно расширить интервал, на котором возможно вычислить функцию Yn (x) с
Am (n, x) начнут увеличиваться, когда
относительной погрешностью 10 20 , взяв N 100 (см. вычисление ошибок J n (x) ).
1 (4n 2 (4m 3) 2 ) (4n 2 (4m 1) 2 ) 1 x2 128 (2m 1) m
n 100, x n
7.3.
m x n2
Асимптотические разложения Ганкеля (Hankel’s expansion)
J n ( x), Yn ( x) Используются асимптотические разложения Ганкеля [2] для функций Бесселя при x + и ограниченном n.
Заметим, что AN есть относительная ошибка вычисления, так как I1 есть величина порядка единицы. Теперь найдём, каким нужно выбирать количество членов ряда для получения нужной относительной ошибки. Am 1 (4n 2 (4m 3) 2 ) (4n 2 (4m 1) 2 ) n4 1 2 Am1 x 128 (2m 1) m 16 x 2 m 2 16m 2
n (1) m (n,2m) n (1) m (n,2m 1) 2 cos( x ) sin( x ) 2m x 2 4 m 0 (2 x ) 2 4 m 0 (2 x) 2 m 1 2 n (1) m (n,2m) n (1) m (n,2m 1) sin( x ) cos( x ) Yn ( x) ~ 2 4 m 0 (2 x ) 2 m 2 4 m 0 x (2 x) 2 m 1 J n ( x) ~
n, m 4n
2
1 4n 2 3 ... 4n 2 (2m 1) 2 2 2 m m!
AN
1 10 20 2 16 N N ! N 9
Для любого значения можно подобрать свою величину N .
Ряды, которые содержат данные формулы, расходятся. В связи с этим знак бесконечности у суммы нужно понимать как суммирование до тех пор, пока члены ряда уменьшаются.
При значениях аргумента x n 2 необходимое значение N сильно увеличивается, и в добавление к этому метод становится неустойчивым по отношению к ошибкам выполняемых арифметических действий. Однако для небольших значений индекса попробуем расширить область применимости метода.
7.3.1. Ошибка вычисления
Исследуем при AN n 100, x n . AN 1 (4n 2 (4 N 3) 2 ) (4n 2 (4 N 1) 2 ) n4 n2 2 2 2 AN 1 x 128 (2 N 1) N 16 x N 16 N 2
Исследуем ошибки для значений аргумента x n 2 . Введём следующие обозначения. J n ( x) ~
2 n n cos( x ) I1 sin( x ) I2 x 2 4 2 4
AN
n2N 100 2 N N 10 20 2 2 16 N ! 16 N ! N
N 90
(1) m (n,2m) Am (n, x) (2 x) 2 m m 0 m 0
I1
Оценку I1 сделаем на компьютере: I1 2.32 ~ 1 . Следовательно, AN есть относительная ошибка.
123
124
2 1 2 n J n ( x) iYn ( x) 1 i 2 , i p iq 2 2 x J n ( x) iYn ( x) x 3 2 n 2 2( x i ) 2 5 2 n 2 2( x 2 i ) 2( x 3 i ) ...
При уменьшении n , или увеличении x , при выборе N 90 , относительная ошибка будет уменьшаться (при этом I1 ~ 1 ). Алгоритм вычисляет последовательно Am и сравнивает с нужной ошибкой, при этом N определяется автоматически. Теперь рассмотрим I 2 . Заметим, что I 2 по порядку величины есть I 2 ~ I1
4n 2 1 , 8x
I 2 I1
Вынесением множителя
4n 2 1 из I 2 можно повторить все рассуждения применённые 8x
к I1 .
W , J n ( x ) q p f ( ) n
Таким образом, выбрав то же число членов ряда в I 2 , мы не увеличим относительной ошибки вычисления функции.
7.4.
Метод Стида (Steed’s method) вычисления J n ( x), Yn ( x)
p fn ; q
Yn ( x) J n ( x) . Первая непрерывная дробь считается до N1-го уровня, вторая до N2-го уровня. Первая
Рассматриваемый в этом разделе метод вычисления функций Бесселя описан в [4,5]. Он применяется для вычислений значений этих функций на аргументах, близких к их индексам.
непрерывная дробь сходится быстро для x n , вторая для x n . Чем больше отклонение аргумента от значения x0 n в ту или иную сторону, тем большими нужно выбирать числа N1 и N2, чтобы обеспечить нужную точность вычислений.
Решается система из 4-х уравнений относительно J n (x) , Yn (x) и их производных.
Знак J n ( x) берётся равным знаку непрерывной дроби f n во втором уравнении. Этот
2 , W J n ( x) Yn ( x) Yn ( x) J n ( x) x
метод позволяет находить значения функций Бесселя J n ( x ), Yn ( x ) для значений аргумента x n .
J n ( x) n J n1 ( x) n 1 fn , 1 J n ( x) x J n ( x) x 2(n 1) 2( n 2) 1 x 2(n 3) x ... x
7.4.1 Ошибка вычисления J n ( x) и Yn ( x) Поскольку оценка погрешностей усложнена из-за наличия ошибки непрерывной дроби, оценить которую очень сложно, то применяется следующий алгоритм проверки ошибок. Известно, что функции Бесселя удовлетворяют рекуррентным соотношениям: 2n J n1 ( x) J n1 ( x) J n ( x) , x 2n Yn1 ( x) Yn1 ( x) Yn ( x) . x
125
126
Пусть нам нужно оценить точность вычисления функции J n ( x) , тогда вычисляем J n (nz )
заодно функции J n1 ( x) и J n1 ( x) по тому же алгоритму. Подставляем в рекуррентное соотношение. Если оно выполняется с некоторой ошибкой, то это и есть ошибка
(nz ) n exp(n 1 z 2 ) exp(Vn ) e n n!(1 z 2 )1/ 4 [1 1 z 2 ]n
,
Vn V1 V2 V3 V4 V5 V6 V7 V8 ... ,
вычисления функции. Таким способом можно получить следующие интервалы, в которых данный алгоритм даёт относительную ошибку вычисления 10 20 при выборе
V1
числа итераций дробей равным 500: 1) 10 2 n 10 3 , n 10 x n 400
V3
2) 10 3 n 10 4 , n 10 x n 100 3) 10 4 n 10 5 , n 10 x n 10
1 2 3z 2 4z2 z4 , 2 , V2 2 3/ 2 24n (1 z ) 16n 2 (1 z 2 ) 3 16 1512 z 2 3654 z 4 375 z 6 1 16 , 3 5760 n (1 z 2 ) 9 / 2 V4
32 z 2 288 z 4 232 z 6 13 z 8 , 128 n 4 (1 z 2 ) 6
Данные интервалы можно расширить более мелким разбиением значений индекса n .
7.5.
1 (67599 z10 1914210 z 8 4744640 z 6 1891200 z 4 322560 n 5 (1 z 2 )15 / 2 1 , 78720 z 2 256) 1260 n 5 z2 V6 (48 2580 z 2 14884 z 4 17493 z 6 4242 z 8 103 z 10 ), 6 192 n (1 z 2 ) 9
V5
Первое разложение Мейсселя (Meissel’s first expansion)
Рассматриваемый в этом разделе метод приведен в [6]. Перепишем уравнение Бесселя в следующем виде, сделав замену переменных. z2
d 2 J n (nz ) dJ (nz ) z n n 2 (1 z 2 ) J n (nz ) 0 dz dz
V7
Решение его ищем в виде J n (nz )
u ( z ) n u0 u1
1 (881664 z 2 99783936 z 4 1135145088 z 6 3440640 n 7 (1 z 2 ) 21/ 2
2884531440 z 8 1965889800 z10 318291750 z12 5635995 z14 2048)
nn exp u ( z )dz n! z
V8
u 2 u 3 u 4 u 5 u 6 u 7 u8 u 9 ... n n 2 n 3 n 4 n 5 n 6 n 7 n8
z2 (1024 248320 z 2 5095936 z 4 24059968 z 6 4096 n (1 z 2 )12 8
34280896 z 8 15252048 z 10 1765936 z 12 23797 z14 ),...
находим функции u0 , u1 , u2 ,... с помощью подстановки выражения для J n (nz ) в
Аналогично для функции Бесселя второго рода получаем
уравнение Бесселя. Yn (nz )
z 1 z2 4z z2 4 z 10 z 3 z 5 , u1 , u2 , u3 , u0 z 2(1 z 2 ) 8(1 z 2 )5 / 2 8(1 z 2 ) 4 u4
5
7
3
5
7
e n (n 1)![1 1 z 2 ] n exp(Vn )
(nz ) n (1 z 2 )1 / 4 exp(n 1 z 2 )
Vn V1 V2 V3 V4 V5 V6 V7 V8 ...
64 z 560 z 456 z 25z 16 z 368 z 924 z 347 z 13z , u5 ,... 128(1 z 2 )11 / 2 32(1 z 2 ) 7 3
9
7.5.1. Ошибка вычисления J n (x) и Yn (x)
Интегрируем u ( z ) и получаем выражение для функции Бесселя первого рода.
127
Оценим ошибку применённых формул.
128
1 , 1680 n 7
,
z V9
x , n
1 (106 z 2 108 z 4 1010 z 6 1012 ( z 8 z10 z12 ) 1010 z14 108 z16 106 z18 ) 107 n 9 (1 z 2 ) 27 / 2
Это и есть нужная относительная ошибка вычисления, обозначим её за Потребуем, чтобы величина ошибки не превосходила 10 f ( z)
20
.
1 (10 6 z 2 108 z 4 1010 z 6 10 7 n 9 (1 z 2 ) 27 / 2
10 ( z z z ) 10 z 10 z 10 z ) 10 12
f (z ) .
8
10
12
10
14
8
16
6
18
Рисунок 5. Графики f(z) для n = 3000 и n = 104.
20
Построим график функции f ( z ) для различных значений индекса n . См. Рис.3-6.
Рисунок 6. График f(z) для n = 105.
Анализируя графики, можно выбрать следующие интервалы для n и x , на которых
Рисунок 3. Графики f(z) для n = 100 и n = 300.
данный метод приближает функции Бесселя с относительной погрешностью менее 10 20 . 1) 100 n 300, x 0.1 n 2) 300 n 600, x 0.3 n 3) 600 n 1000, x 0.5 n 4) 1000 n 3000, x 0.6 n 5) 3000 n 104 , x 0.8 n 6) 104 n 105 , x 0.9 n 7) 105 n , x 0.98 n
Рисунок 4. Графики f(z) для n = 600 и n = 1000.
129
130
cot15 (256 78720 sec 2 1891200 sec 4 322560 n 5 4744640 sec 6 1914210 sec8 67599 sec10 ),
Данной относительной погрешности достаточно для тестирования функций на
Q3
переменных типа long double, а следовательно и double и float. Для тестирования функций на переменных типа double требуется меньшая относительная погрешность, а
cot 21 (881664 sec 2 99783936 sec 4 3440640 n 7 1135145088 sec 6 2884531440 sec8 1965889800 sec10
Q4
для тестирования float ещё меньшая.
7.6.
Второе разложение Мейсселя (Meissel’s Second expansion)
318291750 sec12 5635995 sec14 2048),...
Приведенное здесь разложение также взято из [6]. J n (n sec )
Yn (n sec )
sec
7.6.1. Ошибка вычисления J n (x)
2 cot 1 exp Pn cos Qn , n 4
P5 ~
2 cot 1 exp Pn sin Qn . n 4
Q5 ~
1 cos 1 x , cot cos n sin sec 2 1
1 z2 1
, z
x n
Относительная ошибка вычисления есть
cot (4 sec 2 sec 4 ), 16 n 2 6
P5 Q5 tg (Qn ) 4
cot12 P2 (32 sec 2 288 sec 4 232 sec 6 13 sec8 ), 128 n 4
cot 3 (2 3 sec 2 ) 24n x 2 1 n n 1 arccos 3/ 2 n 2 x 24n x 1 n
Qn Q1 n(tan )
cot 18 (48 sec 2 2580 sec 4 14884 sec 6 17493 sec 8 192 n 6 4242 sec10 103 sec12 ), P3
cot 24 sec 2 (1024 248320 sec 2 5095936 sec 4 4096 n 8 24059968 sec 6 34280896 sec8 15252048 sec10 P4
Учитывая, что z
1765936 sec12 23797 sec14 ),...
Q1 n(tan )
cot 3 (2 3 sec 2 ), 24n
x 2 3 n
z 2 1 arccos 1 z
1
24n z 2 1
3/ 2
2 3z 2
Построим графики функции f ( z , n) P5 ( z , n) Q5 ( z , n) tg (Qn ( z , n) от z для различных значений индекса n . См. Рис. 7, 8.
cot 9 (16 1512 sec 2 3654 sec 4 375 sec 6 ), 5760n 3
131
132
2
x , перепишем последнее соотношение. n
Qn n
Qn Q1 Q2 Q3 Q4 ...
Q2
cot 27 (10 6 z 2 108 z 4 1012 ( z 6 z 8 z 10 z12 ) 1011 z 16 108 z 18 ) , 10 7 n 9 cot
Pn P1 P2 P3 P4 ... P1
cot 30 (103 z 2 10 7 z 4 109 ( z 6 z 8 z 10 z12 z14 ) 108 z16 105 z18 ) , 103 n10
4
) как функции
7.6.2 Ошибка вычисления Yn ( x) Относительная ошибка вычисления есть
P5 Q5 cot(Qn ) 4
Построим графики функции f ( z , n) P5 ( z , n) Q5 ( z , n) cot(Qn ( z , n) ) как функции 4 от z для различных значений индекса n . См. Рис. 9, 10.
Рисунок 7. Графики f(z) для n = 100 и n = 1000.
Из первого графика видно, что при z 5 относительная ошибка меньше 10 20 .
Рисунок 9. Графики f(z) для n = 100 и n = 1000.
Из первого графика видно, что при z 6 относительная ошибка меньше 10 20 .
Рисунок 8. Графики f(z) для n = 104 и n = 105.
Анализируя графики, можно выбрать следующие интервалы для n и x , на которых данный метод приближает функции Бесселя с относительной погрешностью менее
10 20 . 1) 100 n 1000, x 5 n 2) 1000 n 10 4 , x 1.6 n 3) 10 4 n 105 , x 1.14 n 4) 105 n , x 1.03 n Для
другой
относительной
Рисунок 10. Графики f(z) для n = 104 и n = 105.
погрешности
можно
подобрать
свои
интервалы.
133
134
3. Если значение одной из функций Yn ( x ) при данном значении аргумента x 0 велико настолько, что выходит за пределы интервала чисел с плавающей точкой, то данная функция должна возвращать -∞. Обозначим за M максимальное представимое число. Из асимптотики поведения функции Y0 ( x) в
Анализируя графики, можно выбрать следующие интервалы для n и x , на которых данный метод приближает функции Бесселя с относительной погрешностью менее
10 20 : 1) 2) 3) 4)
M нуле следует, что для аргументов 0 x 2 exp (здесь — 2 постоянная Эйлера) функция должна возвращать . Заметим, что, например, для чисел типа float, double, long double верна следующая оценка: M 2 exp , а значит для всех представимых аргументов x 0 2 Y0 ( x) M . Для функции Y1 ( x) должно выполняться: выполнено
100 n 1000, x 6 n 1000 n 104 , x 1.7 n 104 n 105 , x 1.12 n 105 n , x 1.03 n
Для другой относительной погрешности можно подобрать свои интервалы.
8. Требования к реализациям функций Бесселя 8.1.
Y1 ( x ) , 0 x
Область определения функций
.
От
8.3.
1. Если значение одной из функций J n ( x ), n 0 при данном значении аргумента
n
представимое положительное число. Из асимптотики J n ( x)
J 0 ( x) 1
1 n
1 x , x 0 n! 2
Это свойство должно быть выполнено для всех значений аргумента.
8.4.
2. J n ( x) (1) n J n ( x)
Таким образом, от функции J n ( x) нужно требовать: J n ( x ) 0, x 2 ( n! ) .
3. Yn ( x) (1) n Yn ( x)
2. Функция J 0 ( x) имеет в точке x 0 экстремум J 0 ( x) 1 . Следовательно, нужно
4. Рекуррентные соотношения:
2n 4.1. J n1 ( x) J n1 ( x) J n ( x) x
требовать, чтобы в некоторой окрестности нуля определяемой неравенством 1 J 0 ( x) функция возвращала единицу. Возьмём в разложении Тейлора
2n 4.2. Yn1 ( x) Yn1 ( x) Yn ( x) x
2
x функции J 0 ( x) первые два члена: J 0 ( x) 1 , x 0 . Из неравенства 2
x 1 J 0 ( x ) 1 1 2
2
интервал
для
аргумента
x 2 x 2 . Таким образом, от функции 2
8.5.
Сохранение знака
Знак значения реализации функции для всех аргументов должен совпадать со знаком значения самой функции, кроме тех значений аргумента, у которых единица последнего разряда становится больше величины интервала смены знака.
J 0 ( x) нужно требовать: J 0 ( x ) 1, x 2 . 135
Некоторые соотношения
1. J 0 ( x) J 0 ( x), J1 ( x) J1 ( x), J n ( x) (1) n J n ( x)
функция должна возвращать 0. 1 n
следующий
требовать:
Из всех функций только для одной можно дать точную оценку для области значений, причём это всего лишь оценка сверху.
точкой, то данная функция должна возвращать 0. Обозначим за ε минимальное
получим
нужно
Область значений функций
x 0 мало настолько, что выходит за пределы интервала чисел с плавающей
1 J 0 ( x)
Yn ( x)
( n 1)! Yn ( x ) , 0 x 2 . M
Точки выхода за интервал представимых чисел
следует, что для аргументов x 2 ( n!)
функции
1 n
1. J n ( x ) определена для всех значениях аргумента x . 2. Yn ( x ) определена для значений аргумента x 0 .
8.2.
2
M
136
8.6.
Если порядок Y больше порядка X, то
Значения на особых числах с плавающей точкой
x J 0 ( x)
0
error error1 error2 .
+0
0
0
Если в результате сложения порядок результирующего числа больше порядка слагаемых, то ошибку нужно разделить на base .
NaN NaN NaN
0 0 0
NaN NaN NaN
J n ( x), n 2
NaN
+0
J n ( x), n 1
NaN
Y0 ( x)
NaN NaN NaN
Y1 ( x) Yn ( x)
0
+0, n 2k , k -0, n 2k 1, k
J1 ( x)
1 error1 . base e 2e1
0
0
+0 1 +0
error error2
0 0
-0 1 -0 +0, n 2k , k -0, n 2k 1, k
NaN NaN NaN
Если порядки равны, то
9.2.
Ошибка вычитания чисел X и Y
Если порядок X больше порядка Y, то
error error1
Таблица 2. Значения функций Бесселя для особых чисел с плавающей точкой.
1 error2 . base e1e 2
9. Ошибки выполнения арифметических операций
Если e1 e2 P 1 , то ошибка увеличивается на единицу.
Во время всех вычислений одновременно вычисляется оценка ошибки, связанной с ограниченностью количества хранимых значимых цифр P в числах, с которыми оперируют используемые алгоритмы. Обозначим основание системы счисления за base . Оценка ошибка хранится в виде целого числа R и вычисляется по следующей формуле.
Если порядок Y больше порядка X, то
R error base P
error error2
Если e2 e1 P 1 , то ошибка увеличивается на единицу. Если порядки равны, то error error1 error2 .
Пусть производится некоторая операция с числами X и Y. Обозначим как e1 порядок числа X, как e2 обозначим порядок числа Y, как f1 обозначим первую значащую цифру числа X, а как f 2 — первую значащую цифру числа Y. Пусть ошибка X равна error1 , ошибка Y равна error2 .
9.3.
Далее приводятся алгоритмы вычисления ошибок, которые позволяют оценить порядок возникающих ошибок операций. Сначала ошибки всех чисел инициализируются единицей, так как за некоторое число операций в результате округления у чисел появится погрешность не превосходящая 10 P .
9.4.
9.1.
Ошибка умножения чисел X и Y error
error1 (1 f1 ) error2 (1 f 2 ) base base
Ошибка деления чисел X и Y error
Ошибка сложения чисел X и Y
error1 base error2 base f1 f1 ( f2 )2
Если f1 / f 2 1 , то ошибку нужно разделить на base .
Если порядок X больше порядка Y, то
error error1
1 error1 . base e 2e1
9.5.
1 error2 . base e1e 2
Статистика ошибок вычисления J n ( x)
Далее в Табл. 3 приведены оценки ошибки используемых методов в виде значений R для различных интервалов, на которых вычисляются функции. 137
138
Метод Представление в виде степенного ряда
Интервал n 100, x n
n 100, x n n 100, x n
Асимптотическое разложение Ганкеля
n 100, x n 2
10 6 103
Метод Стида
10 2 n 10 3 , n 10 x n 400
10 9
10 3 n 10 4 , n 10 x n 100
10 9
10 4 n 10 5 , n 10 x n 10 100 n 300, x 0.1 n 300 n 600, x 0.3 n 600 n 1000, x 0.5 n 1000 n 3000, x 0.6 n
10 9
Первое разложение Мейсселя
3000 n 10 4 , x 0.8 n Второе разложение Мейсселя
10 4 n 105 , x 0.9 n 100 n 1000, x 5 n 10 4 n 105 , x 1.14 n
10 6
10 n , x 1.03 n
10 6
5
Второе разложение Мейсселя
10.
Статистика ошибок вычисления Yn ( x)
Далее в Табл. 4 приведены оценки ошибки используемых методов в виде значений R для различных интервалов, на которых вычисляются функции. Метод Представление в виде степенного ряда
Интервал n 100, x n
n 100, x n n 100, x n
R 108 10 2
Асимптотическое разложение Ганкеля
n 100, x n
Метод Стида
10 2 n 10 3 , n 10 x n 400
10 9
10 n 10 , n 10 x n 100
10 9
10 n 10 , n 10 x n 10
10 9
3
4
2
4
5
10 4 103 103 103 103
10 4
1000 n 10 4 , x 1.7 n
10 4 105
10 4 n 105 , x 1.12 n
10 6
10 n , x 1.03 n
10 6
Таблица 4. Значения оценок ошибок арифметических действий при вычислении Yn ( x ) .
Таблица 3. Значения оценок ошибок арифметических действий при вычислении J n ( x ) .
9.6.
10 4 n 105 , x 0.9 n 100 n 1000, x 6 n
5
10 4
1000 n 10 , x 1.6 n
100 n 300, x 0.1 n 300 n 600, x 0.3 n 600 n 1000, x 0.5 n 1000 n 3000, x 0.6 n 3000 n 10 4 , x 0.8 n
10 4 103 103 103 103
10 4 105
4
Первое разложение Мейсселя
R 108 10 2
10 6 103
139
Структура разработанной реализации
В результате проведенной работы была разработана реализация функций Бесселя первого и второго рода на языке C. Реализация выполнена в виде набора функций, работающих с данными типа unifloat (числа с плавающей точкой произвольной точности) и размещенных в файле bessel.c. Разработанные функции перечислены в Табл. 5. Декларация функции Пояснение unifloat bessel_j0(unifloat x, Функция Бесселя первого рода индекса 0 int method) unifloat bessel_j1(unifloat x, Функция Бесселя первого рода индекса 1 int method) unifloat bessel_y0(unifloat x, Функция Бесселя второго рода индекса 0 int method) unifloat bessel_y1(unifloat x, Функция Бесселя второго рода индекса 1 int method) unifloat bessel_jn(int n, Функция Бесселя первого рода индекса n unifloat x, int method) unifloat bessel_yn(int n, Функция Бесселя второго рода индекса n unifloat x, int method) Вспомогательные функции unifloat powerseries_jn(int n, Реализация ряда Тейлора для Jn(x) unifloat x) unifloat powerseries_yn(int n, Реализация ряда Тейлора для Yn(x) unifloat x)
140
unifloat Hankel_jn(int n, unifloat x) unifloat Hankel_yn(int n, unifloat x) unifloat Steed_jn(int n, unifloat x) unifloat Steed_yn(int n, unifloat x) unifloat MeisselFirst_jn(int n, unifloat x) unifloat MeisselFirst_yn(int n, unifloat x) unifloat MeisselSecond_jn(int n, unifloat x) unifloat MeisselSecond_yn(int n, unifloat x)
Реализация разложения Ганкеля для Jn(x)
[2]
Реализация разложения Ганкеля для Yn(x)
[3] [4]
unifloat Stirling_series(int n)
Вспомогательная функция для вычисления факториала
unifloat UFsqrt(unifloat x)
Вспомогательная функция извлечения корня Вспомогательная функция возведения в степень
unifloat UFpow(int n, unifloat x) complexnum complex_add(complexnum complexnum y) complexnum complex_sub(complexnum complexnum y) complexnum complex_mul(complexnum complexnum y) complexnum complex_div(complexnum complexnum y)
Реализация метода Стида для Jn(x)
[5]
Реализация метода Стида для Yn(x) Реализация первого разложения Мейсселя для Jn(x) Реализация первого разложения Мейсселя для Yn(x) Реализация второго разложения Мейсселя для Jn(x) Реализация второго разложения Мейсселя для Yn(x)
[6]
Сложение комплексных чисел x,
Вычитание комплексных чисел x,
Умножение комплексных чисел x,
Деление комплексных чисел x,
Таблица 5. Список функций, разработанных при реализации вычислений функций Бесселя.
11.
Заключение
В результате данной работы исследованы и реализованы методы вычислений функций Бесселя, проанализирована их точность и приведена статистика арифметических операций. Составлены требования к реализациям функций.
Литература [1]
http://www.linuxbase.org/spec
141
142
У. Джоунс, В. Трон. Непрерывные дроби. Аналитическая теория и приложения. М: Мир, 1985. Г. Н. Ватсон. Теория Бесселевых функций. М: ИЛ, 1949. W. H. Press, S. A. Teukolsky, W. T. Vetterling, and B. P. Flannery. Numerical Recipes in Fortran 77: The Art of Scientific Computing. Cambridge University Press, 1992. F. A. Chishtie, S. R. Valluri, K. M. Rao, D. Sikorski, and T. Williams. The Analysis of Large Order Bessel Functions in Gravitational Wave Signals from Pulsars. In Proc. Of the 19-th International Symposium on High Performance Computing Systems and Applications, 2005. M. Abramowitz and I. A. Stegun, eds. Handbook of Mathematical Functions with Formulas, Graphs, and Mathematical Tables. Dover Publications, 1965.
Тестирование в условиях неполной информации. Подход к разработке спецификаций и генерации тестов А. С. Камкин Институт системного программирования РАН (ИСП РАН), Б. Коммунистическая, 25, Москва, Россия E-mail:
[email protected]
Аннотация В статье исследуются вопросы функционального тестирования программных систем в условиях неполной информации. Неполнота информации рассматривается в двух аспектах: статическом, связанном с неполнотой функциональных требований, по которым разрабатываются спецификации и тесты, и динамическом, связанном с неполнотой информации о состоянии целевой системы в процессе тестирования. В работе предлагается подход к разработке функциональных спецификаций и генерации функциональных тестов, основанный на использовании неопределенных значений для моделирования состояния целевой системы, а также трехзначной логики Клини для работы с неполными требованиями и описания свойств системы. В качестве базовой технологии используется технология тестирования UniTESK.
1. Введение В последнее время в науке и технике интенсивно развиваются и широко распространяются методы, оперирующие с неполной или нечеткой информацией1 [1]. Примерами теорий, в рамках которых разрабатываются подобные методы, являются математическая статистика, теория нечетких множеств, теория возможностей и многие другие. Методы анализа и обработки неполной информации хорошо зарекомендовали себя в искусственном интеллекте, теории баз данных, системном анализе и исследовании операций. Сегодня их активно используют для моделирования сложных физических, экономических и социальных явлений [2]. Таким образом, работа с неопределенной и нечеткой информацией постепенно становится уделом точных наук. Программной инженерии, претендующей на точность и методичность инженерной дисциплины, также приходиться иметь дело с неполной информацией. В проектах по разработке программного обеспечения неопределенность возникает на самых ранних фазах. Размытый и туманный характер носят требования к программной системе,
1 В дальнейшем эпитет неполный используется в отношении как, собственно, неполной, так и нечеткой информации, как и его синонимы неопределенный и не полностью определенный. 143
которые приходится многократно уточнять, дополнять и согласовывать. После доработок требования используются на следующей фазе, на которой, как правило, имеют дело с более низким уровнем абстракции, поэтому требования вновь кажутся неполными, их снова приходится дорабатывать. Таким образом, на каждой фазе проекта исходная информация оказывается не полностью определенной. В работе рассматривается лишь один из этапов разработки программного обеспечения — тестирование. Обычно на этапе тестирования располагают более-менее завершенным продуктом и достаточно проработанными требованиями. Казалось бы, что на этом этапе в требованиях не должно быть никаких неопределенностей, но, как показывает практика, это не так. Использование требований для целей тестирования в очередной раз выявляет их недостатки. Требования после многочисленных доработок вновь оказываются неполными. С другой стороны, в процессе тестирования возможна ситуация, когда состояние целевой системы не полностью определено. Например, такая ситуация может возникнуть, когда состояние целевой системы скрыто, а начальное состояние в требованиях не определено. Неполнота информации о состоянии целевой системы может приводить к невозможности адекватной оценки правильности поведения целевой системы. Перед тем, как оценивать поведение, необходимо получить представление о состоянии целевой системы путем подачи тестовых воздействий и анализа реакций на них. Статья выполнена в контексте тестирования на основе моделей (MBT, model based testing)2, более точно, в контексте технологии тестирования UniTESK [3-5], хотя рассматриваемые в ней вопросы актуальны не только для этой технологии. Модель представляет собой некоторое достаточно абстрактное отображение структуры и поведения целевой системы, созданное на базе требований [6]. В процессе тестирования состояние модели можно интерпретировать, как располагаемую информация о состоянии целевой системы, сформулированную в терминах требований. В работе предлагается подход к разработке функциональных спецификаций и генерации функциональных тестов, позволяющий работать с неполными требованиями к целевой системе и неполной информацией о ее состоянии. Подход основан на использовании неопределенных значений для моделирования состояния целевой системы, а также трехзначной логики Клини (Kleene) [7] для работы с требованиями и описания свойств системы. Результаты работы получены в ходе выполнения проекта по разработке открытого тестового набора OLVER для операционной системы Linux [8]. Статья построена следующим образом. Во втором, следующем за введением, разделе делается краткий обзор технологии тестирования UniTESK. В третьем разделе рассматриваются вопросы, связанные с неполнотой функциональных требований и
2
Часто вместо термина тестирование на основе моделей используется близкий термин тестирование на основе (формальных) спецификаций (specification based testing, specification-driven testing). 144
неполнотой информации о состоянии целевой системы в процессе тестирования. В четвертом разделе описывается подход к спецификации систем на основе неопределенных значений, уточняемых типов и трехзначной логики Клини. В пятом разделе рассматриваются вопросы построения тестовых последовательностей по неопределенным обобщенным моделям. В шестом разделе предлагается простое расширение технологии тестирования UniTESK на примере инструмента разработки тестов CTesK [9]. В заключении делается краткое резюме работы.
2. Краткий обзор технологии тестирования UniTESK Технология тестирования UniTESK [3-5] была разработана в Институте системного программирования РАН [10] на основе опыта, полученного при разработке и применении технологии KVEST (kernel verification and specification technology) [11]. Общими чертами этих технологий являются использование формальных спецификаций в форме пред- и постусловий интерфейсных операций и инвариантов типов данных для автоматической генерации оракулов (компонентов тестовой системы, осуществляющих проверку правильности поведения целевой системы), а также применение конечно-автоматных моделей для построения последовательностей тестовых воздействий (тестовых последовательностей). В отличие от технологии KVEST, в которой для спецификации требований использовался язык RSL (RAISE specification language) [12], технология UniTESK использует расширения широко известных языков программирования.
установления связи между тестовой системой и реализацией целевой системы — медиатор. Рассмотрим подробнее каждый из указанных компонентов. Обходчик является библиотечным компонентом тестовой системы UniTESK и предназначен вместе с итератором тестовых воздействий для построения тестовой последовательности. В основе обходчика лежит алгоритм обхода графа состояний обобщенной конечно-автоматной модели целевой системы (конечного автомата, моделирующего целевую систему на некотором уровне абстракции). Обходчики, реализованные в библиотеках инструментов UniTESK, требуют, чтобы обобщенная конечно-автоматная модель целевой системы, была детерминированной3 и имела сильно-связный граф состояний. Итератор тестовых воздействий работает под управлением обходчика и предназначен для перебора в каждом достижимом состоянии конечного автомата допустимых тестовых воздействий. Итератор тестовых воздействий автоматически генерируется из тестового сценария, представляющего собой неявное описание обобщенной конечноавтоматной модели целевой системы.
2.1. Архитектура тестовой системы UniTESK Архитектура тестовой системы UniTESK была разработана на основе многолетнего опыта тестирования промышленного программного обеспечения из разных предметных областей и разной степени сложности. Учет этого опыта позволил создать гибкую архитектуру, основанную на следующем разделении задачи тестирования на подзадачи.
Построение последовательности тестовых воздействий (тестовой последовательности), нацеленной на достижение нужного покрытия.
Создание единичного последовательности.
Установление связи между тестовой системой и реализацией целевой системы. Проверка правильности поведения целевой системы в ответ на единичное тестовое воздействие.
тестового
воздействия
в
рамках
тестовой
Для решения каждой из этих подзадач предусмотрены специальные компоненты тестовой системы (см. Рисунок 1): для построения тестовой последовательности и создания единичных тестовых воздействий — обходчик и итератор тестовых воздействий, для проверки правильности поведения целевой системы — оракул, для
Рисунок 1. Архитектура тестовой системы UniTESK.
Оракул оценивает правильность поведения целевой системы в ответ на единичное тестовое воздействие. Он автоматически генерируется на основе формальных спецификаций, описывающих требования к целевой системе в виде пред- и постусловий интерфейсных операций и инвариантов типов данных. Медиатор связывает абстрактные формальные спецификации, описывающие требования к целевой системе, с конкретной реализацией целевой системы. Медиатор преобразует единичное тестовое воздействие из спецификационного представления в реализационное, а полученную в ответ реакцию — из реализационного представления в
3
145
Исключение составляет обходчик ndfsm [13], позволяющий обходить графы состояний для некоторого класса недетерминированных конечных автоматов. 146
спецификационное. Также медиатор синхронизирует состояние спецификации с состоянием целевой системы. Трасса теста отражает события, происходящие в процессе тестирования. На основе трассы можно автоматически генерировать различные отчеты, помогающие в анализе результатов тестирования.
3. Полнота функциональных требований В программной инженерии разработка, тестирование, а также сопровождение и эксплуатация программной системы осуществляются на основе требований. На ранних этапах жизненного цикла программной системы требования носят размытый характер, описывая в общих чертах концептуальный замысел разрабатываемой системы. На протяжении всего жизненного цикла системы требования дорабатываются и, пока система используется, их нельзя назвать полностью завершенными. Поскольку в работе исследуются вопросы функционального тестирования, в ней рассматриваются только функциональные требования — требования, описывающие функциональность системы, то есть что она должна делать, но не описывающие как она должна это делать4. Основными свойствами требований, характеризующими их качество, являются понятность, непротиворечивость и полнота. Мы будем рассматривать только одно из них — полноту, предполагая при этом, что требования понятны и непротиворечивы. В дедуктивных науках теория называется полной, если всякое высказывание, сформулированное в терминах этой теории, может быть либо доказано, либо опровергнуто [14]. В программной инженерии понятие полноты несколько сложнее. Обычно требования рассматриваются в контексте некоторой деятельности и считаются полными, если на их основе можно решить любую задачу в рамках этой деятельности. Понятно, что из-за различия в роде деятельности, полнота требований по-разному воспринимается разными группами лиц, связанных с программной системой. Например, для пользователя системы полнота требований, в первую очередь, означает возможность на их основе осуществить любой вариант использования системы (use case)5; для разработчика системы — правильно ее реализовать; для разработчика тестов — разработать полный набор тестов. Поскольку статья посвящена тестированию, полнота требований в ней рассматривается только с точки зрения разработчика тестов.
3.1. Требования и сценарии взаимодействия с системой Задачу тестирования можно разбить на две основные подзадачи: построение последовательности тестовых воздействий и проверку правильности поведения
4 В дальнейшем для краткости будем опускать эпитет функциональные и употреблять термин требования, понимая под ним именно функциональные требования. 5 Предполается, что варианты использования целевой системы известны, например, описаны в требованиях к системе. 147
целевой системы в ответ на поданные тестовые воздействия. В соответствии с этим, разработка тестов по технологии UniTESK разбивается на разработку тестовых сценариев и разработку спецификаций. Данный раздел посвящен определению полноты требований для разработки тестовых сценариев. Опр. Будем говорить, что требования к целевой системе являются сценарно полными, если они описывают, как на основе интерфейса целевой системы можно реализовать все указанные в них варианты использования, а также воссоздать или идентифицировать все указанные в них ситуации. ■ В сценарно неполных требованиях для некоторых описанных в них ситуаций могут отсутствовать описания способов их достижения или идентификации. В контексте технологии тестирования UniTESK это означает невозможность достичь 100% покрытия тестовых ситуаций, определенных в спецификации на основе требований. Если требования сценарно неполны, значения некоторых атрибутов состояния спецификации оказываются неопределенными на любых последовательностях тестовых воздействий. Опр. Будем говорить, что требования к целевой системе являются детерминированными, если они описывают, как на основе интерфейса целевой системы можно контролировать или наблюдать ее поведение при выполнении всех возможных сценариев взаимодействия с ней. В противном случае будем говорить, что требования являются недетерминированными. ■ Если требования недетерминированны, то возможны сценарии взаимодействия с целевой системой, при выполнении которых нельзя однозначно определить состояние целевой системы. В контексте технологии тестирования UniTESK это означает, что функция вычисления постсостояния спецификации в некоторых ситуациях оказывается недетерминированной. Проиллюстрируем понятие сценарной полноты и детерминизма требований на следующем простом примере. Пример. Целевая система является стеком. Интерфейс целевой системы состоит из следующих функций:
Stack* create(void);
void push(Stack *stack, Object *obj);
Object* pop(Stack *stack).
Пусть требования к целевой системе формулируются следующим образом: Функция create создает пустой стек и возвращает указатель на него. В случае если стек не полностью заполнен, вызов функции push добавляет в него элемент, противном случае, вызов функции push не меняет состояние стека. Вызов функции pop для непустого стека возвращает последний добавленный в него элемент, для пустого стека — NULL.
148
Эти требования не являются сценарно полными, так как не описывают как достичь или идентифицировать указанную в них ситуацию «стек полностью заполнен». Также эти требования не являются детерминированны, так как не позволяют однозначно определить в какое состояние перейдет целевая система после вызова функции push. Если добавить в требования описание ситуации «стек полностью заполнен», указав, например, максимальное возможное число элементов в стеке, требования становятся сценарно полными и детерминированными. ■
изменяется состояние целевой системы при том или ином воздействии на нее. Для возможности определить состояние целевой системы после обработки тестового воздействия важную роль играет детерминизм требований.
3.2. Требования и оценка правильности поведения системы Теперь рассмотрим определение полноты требований в контексте разработки спецификаций. Одна из основных задач разработчика тестов — определить, какое поведение целевой системы следует считать правильным, а какое ошибочным. Ясно, что требования, претендующие на полноту, должны позволять проводить такую классификацию. Опр. Будем говорить, что требования к целевой системе являются контрактно полными, если на их основе можно однозначно классифицировать поведение целевой системы на правильное или ошибочное на любом допустимом сценарии взаимодействия с ней. В противном случае будем говорить, что требования являются контрактно неполными. ■ Контрактная неполнота требований означает, что в оракуле, оценивающем поведение целевой системы, есть «дыры» — возможны ситуации, в которых он не может вынести однозначный вердикт о правильности или ошибочности поведения целевой системы. Проиллюстрируем понятие контрактной полноты требований на следующем примере. Пример. Рассмотрим целевую систему из предыдущего примера. Пусть требования к функции pop() формулируются следующим образом: Вызов функции pop для непустого стека возвращает последний добавленный в него элемент. Эти требования не являются контрактно полными, так как в них не описано, как должна вести себя функция pop для пустого стека. Если добавить в требования описания того, что вызов функции pop для пустого стека должен возвратить NULL, требования становятся контрактно полными. ■
3.3. Неполнота информации в тестировании При разработке тестов часто возникает ситуация, когда нет прямого доступа к внутреннему состоянию целевой системы. Такое тестирование называется тестированием со скрытым состоянием (hidden state testing). В отличие от тестирования с открытым состоянием (open state testing), когда состояние целевой системы можно получить непосредственно через ее интерфейс, при тестировании со скрытым состоянием разработчику тестов необходимо четко представлять как 149
Рисунок 2. Виды неполноты информации, возникающие при тестировании.
Другим видом неполноты информации в тестировании является неопределенность начального состояния целевой системы. Такое тестирование называется тестированием с неизвестным начальным состоянием. В отличие от тестирования с известным начальным состоянием, при тестировании с неизвестным начальным состоянием на целевую систему сначала нужно подать последовательность тестовых воздействий, приводящую ее в некоторое известное состояние. Здесь важную роль играют сценарная полнота и детерминизм требований. Подведем итог относительно различных видов неполноты информации, которые могут возникнуть при тестировании программных систем (см. Рисунок 2). Во-первых, это может быть контрактная неполнота требований. Во-вторых, при тестировании может быть скрыто состояние целевой системы, что не позволяет непосредственно получать состояние целевой системы через ее интерфейс. В-третьих, может быть не определено
150
начальное состояние целевой системы. В-четвертых, требования могут не определять как изменяется состояние целевой системы при выполнении того или иного воздействия или определять это недетерминированным образом. Наконец, реализация целевой системы может быть недетерминированной.
4. Спецификация в условиях неполной информации В данном разделе рассматривается подход к разработке функциональных спецификаций, позволяющий работать с неполными требованиями к целевой системе и неполной информацией о ее состоянии.
4.1. Неопределенные значения и уточняемые типы Поскольку в процессе тестирования возможна ситуация, когда состояние целевой системы не полностью определено, для моделирования состояния системы удобно использовать типы, поддерживающие неопределенные значения. Будем считать, что на множестве значений типов заданы отношения уточнения, являющиеся отношениями частичного порядка. Отношения уточнения позволяют сравнивать информативность значений: чем «больше» значение в отношении уточнения, тем больше информации оно несет (см. Рисунок 3). Введем несколько понятий. ...
...
...
...
Информативность
Также будем считать, что любое значение типа T можно получить за конечное число уточнений неопределенного значения T. Опр. Максимальные значения уточняемого типа T по отношению T называются (полностью) определенными значениями типа T. Значения уточняемого типа T, не являющиеся полностью определенными, называются неопределенными или не полностью определенными значениями типа T. ■ Опр. Пусть T — уточняемый тип, тогда через Tc будем обозначать (полностью) определенный подтип типа T, то есть подтип, состоящий из всех полностью определенных значений типа T. ■ Если некоторое свойство P имеет не полностью определенное значение x, это означает, что на самом деле значением свойства P является одно из полностью определенных значений, уточняющих x, но какое именно неизвестно. Нужно быть аккуратным при сравнении не полностью определенных значений на равенство. С одной стороны, разным неопределенным значениям может соответствовать одно и то же полностью определенное значение, с другой, одному неопределенному значению могут соответствовать разные полностью определенные значения. Пример. Рассмотрим пример уточняемого типа ST, представляющего нечеткое множество значений типа T. Нечеткое множество s определяется трехзначной функцией принадлежности fs: T {true, false, }. fs(x) интерпретируется следующим образом: если fs(x) = true, то x принадлежит множеству s, если fs(x) = false, то x не принадлежит множеству s, если fs(x) = , то неизвестно принадлежит x множеству s или нет. Отношение уточнения естественно определить таким образом: s1 ST s2 тогда и только тогда, когда из того что fs1(x) = true, вытекает, что fs2(x) = true, а и из того, что fs1(x) = false, вытекает, что fs2(x) = false. ■ Опр. Пусть T1, …, Tn — уточняемые типы. Определим на декартовом произведении T1 … Tn отношение уточнения: (x1, …, xn) T1...Tn (y1, …, yn) тогда и только тогда, когда x1 T1 y1, …, xn Tn yn. ■
Рисунок 3. Отношение уточнения значений типа.
Опр. Тип T с заданным на на нем отношением уточнения называется уточняемым типом. Отношение уточнения, заданное на типе T будем обозначать T или просто , если из контекста ясно о каком типе идет речь. ■ Не ограничивая общности рассуждений, будем считать, что у каждого уточняемого типа T существует единственное минимальное по отношению T значение, которое будем обозначать T или просто и называть неопределенным значением типа T.
От функций, определенных на уточняемых типах будет требовать регулярности и полноты: чем определеннее значение аргумента, тем определеннее значение функции, причем полностью определенному значению аргумента соответствует полностью определенное значение функции. Опр. Функция f: T1 T2 называется регулярной, если из того, что x T1 y следует, что f(x) T2 f(y). ■ Опр. Функция f: T1 T2 называется полной, если f(T1с) T2с, то есть из того, что x T1c следует, что f(x) T2c. ■ Обычно в языках программирования и спецификаций есть базовые типы и есть составные типы, значения которых строятся на основе значений других типов.
151
152
Опр. Составной тип называется регулярным, если все его конструкторы являются регулярными функциями. ■ Опр. Составной тип называется полным, если все его конструкторы являются полными функциями. ■
4.2. Неопределенность и трехзначная логика Клини Для не полностью определенных состояний целевой системы, не исключены ситуации, когда из-за недостатка информации некоторые логические условия нельзя отнести ни к истинным, ни к ложным. В таких случаях использование двузначной логики для описания свойств целевой системы представляется не совсем адекватным. Для того чтобы описать логическое условие P, которое может быть неопределенным, с помощью двух значений истинности нужно использовать специальные модальные функции возможности и необходимости: ◊P и □P 6 и описать условие парой (◊P, □P)7. Тогда истинное условие описывается парой (true, true) (необходимо), ложное — парой (false, false) (невозможно), неопределенное — парой (true, false) (возможно, но не необходимо). Описывать логические условия парой модальностей не очень удобно. Естественней положить, что функция истинности может принимать три значения: true (истина), false (ложь) и (неопределенность), а модальные функции возможности и необходимости рассматривать как функции {true, false, } {true, false}, определяемые следующими таблицами истинности: ◊
Подобным образом неопределенное значение интерпретируется в трехзначной логике Клини (Kleene), называемой K3. При получении дополнительной информации значение логического условия может измениться с неопределенного значения на false или true, но невозможно, чтобы значение изменилось с true на false или наоборот. Последнее требование называется требованием регулярности [7]. Если определить на множестве {true, false, } отношение уточнения, как показано на следующем рисунке, то регулярность условия означает его монотонность по отношению уточнения. false
true
Рисунок 4. Отношение уточнения значений истинности в логике K3.
Рассмотрим логические связки , и . Семантику связок можно определять поразному, основным ограничением для нас является требование регулярности. Существуют два основных способа определения семантики связок: сильный (сильная логика K3, сильные связки K3) и слабый (слабая логика K3, слабые связки K3)8.
false true true
false
true
false
true
false
false
false
true
true
true
true
true
false
6 На модальные функции возможности и необходимости обычно налагают некоторые ограничения, например, ◊P □P и □P ◊P. 7 Обычно модальная логика интерпретируется с помощью системы возможных миров (possible worlds): в каждом возможном мире интерпретация задается двухзначной функцией истинности, а модальные функции определяются совокупностью значений истинности в возможных мирах. 153
false true true
true
true
true
false false
Таблица 2. Слабые связки K3.
Таблица 1. Определение модальных функций ◊ и □.
Впервые модальные функции возможности и необходимости подобным образом определил Лукасевич (Łukasiewicz), в трехзначной логике Ł3 [15]. В логике Ł3 неопределенное значение интерпретируется как промежуточное между ложью и истиной. В нашем случае неопределенное значение следует интерпретировать иначе — как неизвестное значение, которое может быть как истиной, так ложью, но какое оно именно неизвестно.
false
□
false
false true
false false
false true
false true
false false
false false
false true
false false
true
true
false
true
false
true
true
true
true
true
false
true
Таблица 3. Сильные связки K3.
8 Возможны и другие варианты определения семантики связок, например, так называемая короткая логика. 154
В дальнейшем мы будем использовать сильный вариант логики K3. Пример. Рассмотрим пример уточняемого типа ST — нечеткое множество значений типа T. Нечеткое множество s определяется трехзначной функцией принадлежности fs: T {true, false, }. fs(x) интерпретируется следующим образом: если fs(x) = true, то x принадлежит множеству s, если fs(x) = false, то x не принадлежит множеству s, если fs(x) = , то неизвестно принадлежит x множеству s или нет. Отношение уточнения естественно определить следующим образом: s1 ST s2 тогда и только тогда, когда из того что fs1(x) = true, вытекает, что fs2(x) = true, а и из того, что fs1(x) = false, вытекает, что fs2(x) = false. ■
5. Тестирование в условиях неполной информации Как уже неоднократно отмечалось, при тестировании возможна ситуация, когда информация о состоянии целевой системы не доступна полностью тестовой системе. Это означает, что соответствующее состояние модели не полностью определено. В данном разделе описывается подход к генерации тестовых последовательностей, позволяющий работать в таких условиях.
5.1. Неопределенные обобщенные модели Для генерации тестовых последовательностей обычно используют не сами модели тестируемых систем, а их обощения, называемые обобщенными моделями. Например, в технологии тестирования UniTESK используются обобщенные конечно-автоматные модели, описанные в тестовых сценариях. Поскольку состояния модели могут быть неопределенными, не исключено что и состояния обобщенной модели (обобщенные состояния) окажутся неопределенными. Пусть M — уточняемый тип, представляющий состояния модели, S — уточняемый тип, представляющий обобщенные состояния, а f: M → S — функция обобщения модели — функция, ставящая в соответствие каждому состоянию модели обобщенное состояние. Будем считать, что функция f является регулярной и полной. В этом случае она задает факторизацию на множестве полностью определенных значений типа M9. Для удобства будем считать, что f(M) = S.
5.2. Генерация тестов на основе неопределенных моделей Теперь рассмотрим естественное обобщение технологии тестирования UniTESK для генерации тестовых последовательностей на основе неопределенных обобщенных моделей.
В случае когда состояние модели неопределено, тестовые воздействия можно разделить на три группы:
тестовые воздействия, которые допустимы для этого состояния;
тестовые воздействия, которые недопустимы для этого состояния;
тестовые воздействия, для которых не известно, допустимы они или нет для этого состояния.
Таким образом, итератор тестовых воздействий для каждого обобщенного состояния определяет нечеткое множество тестовых воздействий. Из общих соображений понятно, что чем определеннее обобщенное состояние, тем определенней должен быть набор тестовых воздействий, которые нужно подать в этом обобщенном состоянии, а для полностью определенных обобщенных состояний, набор тестовых воздействий должен быть полностью определен. Обходчик может подавать только те тестовые воздействия, для которых точно известно, что они являются допустимыми в текущем состоянии модели. В дальнейшем будем предполагать монотонность уточнений состояния целевой системы в процессе тестирования. Это означает, что ни на каком шаге тестирования не происходит потеря информации о состоянии целевой системы, информация может только накапливаться. Монотонность уточнений состояния тесно связана с детерминизмом требований. Пробелы в требованиях, также как и допущения о возможности недетерминированного поведения целевой системы ведут к потере информации в процессе тестирования. На каждом шаге тестирования тестовая система подает на целевую систему некоторое тестовое воздействие. В ответ на которое, целевая система изменяет свое состояние и выдает реакцию. Используя имеющуюся информацию и полученную реакцию, тестовая система корректирует свое представление о состоянии целевой системы (изменяет и уточняет состояние модели), после чего выносит вердикт о правильности или ошибочности поведения целевой системы. Таким образом, каждый шаг тестирования, с одной стороны, связан с модификацией состояния целевой системы, с другой — с получением дополнительной информации о нем. В соответствии с традиционным требованием детерминизма обобщенной конечноавтоматной модели в технологии тестирования UniTESK, в нашем случае естественно потребовать детерминизма только в полностью определенных обобщенных состояниях. Очевидно, что требование детерминизма в не полностью определенных обобщенных состояниях является слишком жестким: неопределенные состояния открыты для уточнения, а возможных уточнений, вообще говоря, может быть несколько. Введем некоторые вспомогательные понятия. Опр. Уточнение состояния целевой системы называется существенным, если оно вызывает уточнение обобщенного состояния. В противном случае, уточнение называется несущественным. ■ После существенного уточнения состояния целевой системы тестовая система переходит на другой информационный уровень. В предположении монотонности
9
Факторизация задается отношением эквивалентности = { (x, y) McMc | f(x) = f(y) }. 155
156
уточнений состояния целевой системы, тестовая система больше не вернется в обобщенное состояние, предшествующее существенному уточнению. Опр. Модификация состояния целевой системы называется существенной, если она вызывает изменение обобщенного состояния. В противном случае, модификация называется несущественной. ■ Недетерминизм в существенных модификациях часто означает, что поведение целевой системы зависит от еще не определенной части состояния и, поскольку проверить правильность поведения в этом случае все равно нельзя, тестовая система должна исключать такие тестовые воздействия пока не накопит информацию о состоянии целевой системы, достаточную чтобы вынести вердикт о правильности или ошибочности поведения целевой системы. Последнее требование можно ослабить. Если в процессе тестирования тестовая система перешла в обобщенное состояние, которое является уточнением одного из ранее пройденных обобщенных состояний, это означает, что тестовая система получила дополнительную информацию о состоянии целевой системы, следовательно, тестовую последовательность до настоящего момента можно рассматривать как носящую вспомогательный, уточняющий характер. Вместо традиционного для технологии тестирования UniTESK требования сильной связности графа состояний обобщенной конечно-автоматной модели, естественно потребовать сильной связности для графа обобщенных состояний, расширенного дугами, ведущих из уточняющих обобщенных состояний в уточняемые. Такие дуги вполне естественны, поскольку находясь в некотором обобщенном состоянии, тестовая система в то же время находится и во всех уточняемых им обобщенных состояниях. 5.2.1. Графы с уточняемыми вершинами В данном разделе вводится понятие графа с уточняющими вершинами, используемое для формального представления неопределенных обобщенных моделей. Вершины графа интерпретируются как обобщенные состояния целевой системы, дуги — как переходы между обобщенными состояниями, а раскраска дуг — как стимулы, инициирующие соответствующие переходы. Опр. Ориентированным графом или просто графом G называется совокупность трех объектов (GV, GX, GE): GV — множество вершин, GX — множество раскрасок, GE GVGXGV — множество дуг. ■ Заметим, что мы не рассматриваем раскраску дуг графа реакциями (для наших целей реакции не существенны), поэтому в дальнейшем будем называть элементы множества GX стимулами. Опр. Граф G называется конечным, если множества GV и GE конечны. ■ Опр. Граф G называется детерминированным, если для любых двух дуг (u1, x1, v1) GE и (u2, x2, v2) GE из того, что u1 = u2 и x1 = x2 следует что v1 = v2. ■
157
Опр. Для графа G говорят, что стимул x GX допустим в вершине u GV, если в графе G существует дуга вида (u, x, v). ■ Опр. Маршрутом в графе G называется любая (возможно пустая) последовательность смежных дуг {(vi, xi, vi+1)}i=0,n-1, где n 0. При этом вершина v0 называется началом маршрута, а вершина vn — его концом. ■ Опр. Обходом графа G называется маршрут в графе G, содержащий все его дуги. ■ Опр. Граф G называется сильно-связным, если для любых двух вершин u, v GV существует маршрут в графе G с началом в вершине u и концом в вершине v. ■ Опр. Если на множестве GV вершин графа G задано отношение уточнения, будем говорить, что граф G является графом с уточняемыми вершинами. ■ Для удобства будем считать, что графы с уточняемыми вершинами всегда содержат полностью неопределенную вершину и являются полными, то есть в них для любой не полностью определенной вершины существует хотя бы одна уточняющая ее полностью определенная вершина. ■ Опр. Подграф G΄ = (GV΄, GX΄, GE΄) графа с уточняемыми вершинами G = (GV, GX, GE) называется полностью определенным, если все его вершины полностью определены, то есть GV΄ GVc. ■ Опр. Подграф G΄ = (GV΄, GX΄, GE΄) графа с уточняемыми вершинами G = (GV, GX, GE) называется его информационной проекцией, если:
вершины из GV΄ попарно несравнимы в отношении ; добаление в GV΄ любой вершины из GV\GV΄ нарушает первое свойство; множество дуг GE΄ содержит все дуги графа G, соединяющие вершины из GV΄.■
Поскольку различные вершины информационной проекции несравнимы в отношении уточнения, будем рассматривать информационные проекции как традиционные графы, то есть графы без заданного на множестве их вершин отношения уточнения. Опр. Информационная проекция графа с уточняемыми вершинами G, содержащая все полностью определенные вершины GV, называется главной. ■ Опр. Граф с уточняемыми вершинами называется детерминированным, если любая его информационная проекция детерминированна. ■ Опр. Граф с уточняемыми вершинами G называется открытым, если для любой его информационной проекции G΄, за исключением главной, существует дуга (u, x, v) GE\GE΄, раскрашенная стимулом, который отличается от стимулов дуг GE΄, выходящих из u. ■
158
Горизонтальные линии разделяют уровни неопределенности, которых на рисунке три. Опр. Граф с уточняемыми вершинами G называется сильно-связным, если для любых двух вершин u, v GV либо существует маршрут, начинающийся в u и заканчивающийся в v, либо v u. ■ Очевидно, что если граф с уточняемыми вершинами является сильно-связным, то любая его информационная проекция является сильно-связной. Опр. Дуга (u, x, v) в графе с уточняемыми вершинами называется уточняющей, если u v. Уточняющая дуга называется строго уточняющей, если u ≠ v. ■ Опр. Маршрут {(vi, xi, vi+1)}i=0,n-1 в графе с уточняемыми вершинами G называется уточняющим маршрутом, если для любых i и j, таких что 0 i < j n, либо vi и vj не сравнимы в отношении , либо vi vj. Уточняющий маршрут называется строго уточняющим, если i, j такие что 0 i < j n и vi vj, vi ≠ v. ■ Опр. Граф с уточняемыми вершинами называется монотонным, если в нем любой маршрут является уточняющим. ■ Опр. Уточняющим обходом графа G называется маршрут в графе G, начинающийся с неопределенной вершины и содержащий все дуги максимального полностью определенного подграфа G. ■ Опр. Строго уточняющий маршрут {(vi, xi, vi+1)}i=0,n-1 в графе с уточняемыми вершинами G называется простым, если никакой его префикс {(vi, xi, vi+1)}i=0,m-1, где m < n, не является строго уточняющим маршрутом. ■ Рассмотрим уточняющий маршрут P = {(vi, xi, vi+1)}i=0,n-1 в графе с уточняемыми вершинами G, начинающийся с неопределенной вершины: v0 = . Его можно представить в виде конкатенации: P = P0 ... PN-1 PN (Pj={(vi, xi, vi+1)}i=kj,kj+1-1, 0 = k0 < … < kN+1 = n), в которой маршруты Pj для 0 j < N являются строго уточняющими, а маршрут PN является уточняющим, но не является строго уточняющим маршрутом, то есть различные вершины маршрута PN несравнимы в отношении уточнения. Опр. Пусть GVj={vkj,…,vkj+1-1}, где 0 j < N, — все вершины маршрута Pj кроме последней, GVN={skN,…,skN+1} — все вершины маршрута PN. Множество вершин GVj, где 0 j N, будем называть уровнем неопределенности j уточняющего маршрута P. При этом для j < N будем говорить, что маршрут Pj уточняет вершины с уровня неопределенности j до уровеня неопределенности j+1. ■ Заметим, что различные вершины одного уровня неопределенности несравнимы в отношении уточнения, поэтому они являются подмножеством множества вершин некоторой информационной проекции.
Рисунок 5. Уточняющий обход графа с уточняемыми вершинами.
Заметим, что уровни неопределенности могут пересекаться. Пример. Рассмотрим граф с множеством вершин {true, false, }{true, false, }, каждая пара из которых соединена дугой. Маршрут P={(, ), (false, ), (true, ), (false, false), (true, )}10, очевидно, является уточняющим. Ему соответствуют три уровня неопределенности:
GV0 = {(, )}; GV1 = {(false, ), (true, )}; GV2 = {(false, false), (true, )}.
Видно, что уровни неопределенности GV1 и GV2 уточняющего маршрута P имеют общую вершину (true, ). ■ 5.2.2. Алгоритмы уточняющего обхода графов В технологии тестирования UniTESK тестовая последовательность строится в результате обхода графа состояний обобщенной конечно-автоматной модели целевой системы. Для тестирования в условиях неполной информации мы предлагаем использовать определяемые ниже алгоритмы уточняющего обхода. Опр. Алгоритмом движения по графу называется алгоритм, который в процессе своей работы строит маршрут в графе. ■ Опр. Алгоритмом уточняющего движения по графу с уточняемыми вершинами называется алгоритм, который в процессе своей работы строит уточняющий маршрут в графе. ■
Рассмотрим следующий рисунок. Сплошные стрелки изображают обычные дуги графа; жирные стрелки изображают дуги внутри одного уровня неопределенности; стрелки, изображенные пунктиром, связывают уточняющие вершины с уточняемыми. 10
159
Здесь для удобства маршрут представлен в виде последовательности вершин. 160
Как и в работе [16], будем считать, что алгоритму предоставляются две специальные внешние операции status(), возвращающая идентификатор текущей вершины, и call(x), которая осуществляет переход из текущей вершины по дуге, помеченной стимулом x. Для детерминированного графа такая дуга, если существует, то единственная. Предусловием операции call(x) является допустимость стимула x в текущей вершине. Маршрут строится алгоритмом как последовательность дуг, проходимых последовательными вызовами операции call(). Опр. Для маршрута в графе вершину будем называть пройденной, если этот маршрут содержит хотя бы одну инцидентную ей дугу. ■ Опр. Для маршрута в графе вершину будем называть завершенной, если этот маршрут содержит все выходящие из вершины дуги. ■ Опр. Неизбыточным алгоритмом называется алгоритм движения по графу, который зависит только от пройденной части графа и допустимости стимулов в текущей вершине. ■ Как и в работе [16], будем считать, что допустимость стимулов алгоритм может определить с помощью специальной внешней операции next(), которая возвращает стимул, неспецифицированным образом выбираемый среди не выбранных ранее стимулов, допустимых в текущей вершине (осуществляя тем самым итерацию стимулов в вершине). Если все стимулы, допустимые в текущей вершине, уже выбирались, будем считать, что next() возвращает специальный стимул . Опр. Алгоритмом уточняющего обхода графа называется алгоритм, который в процессе своей работы строит уточняющий обход графа. ■ Нас будут интересовать только такие алгоритмы, которые останавливаются через конечное число шагов. Рассмотрим общую схему неизбыточных алгоритмов обхода графов.
Операция completed() возврашает true тогда и только тогда, когда в графе все вершины завершены, то есть не существует вершин графа, из которых ведут не пройденные дуги. Операция rollback() строит маршрут из текущей вершины в вершину, в которой есть еще не пройденные дуги. Подходы к реализации этой операции могут быть различными. Алгоритмы, основанные на обходе остова графа, выбирают самую дальнюю от корня (поиск в глубину) или самую ближнюю к корню (поиск в ширину) незавершенную вершину, достижимую из текущей [16]. Другим подходом (жадный алгоритм) является выбор ближайшей достижимой незавершенной вершины, но это требует больших затрат памяти11. Утв. Для графов с уточняемыми вершинами, являющихся:
конечными, монотонными, детерминированными, открытыми, сильно-связными
существует неизбыточный алгоритм уточняющего обхода. Док. Рассмотрим следующий алгоритм, точнее его идею, в рамках определенной ранее схемы. Поскольку граф является монотонным, любой маршрут в нем является уточняющим, следовательно, для него определены уровни неопределенности. Алгоритм хранит в памяти некоторую структуру данных, связанных с текущим уровнем неопределенности, например, подобную описанной в работе [16]. Операция complete() возвращает true тогда и только тогда, когда все пройденные вершины на текущем уровне неопределенности завершены, то есть операция next() для них возвратила . Операция rollback() строит маршрут из текущей вершины в незавершенную вершину текущего уровня неопределенности, в которой есть еще не пройденные дуги. Например, это можно сделать на основе остова [16]. Поскольку различные вершины одного уровня неопределенности несравнимы в отношении уточнения, пройденный на текущем уровне неопределенности граф является подграфом некоторой информационной проекции исходного графа, а следовательно, является детерминированным и сильно-связным что и требуется в [16].
// пока есть незавершенные вершины while(!сompleted()) { // если текущая вершина незавершена if(next(status()) != ) { // подать очередной стимул call(next(status())); } else { // попасть в пройденную незавершенную вершину rollback(); } }
В случае, если в операции call(x) происходит выбор дуги в вершину, уточняющую одну из вершин текущего уровня неопределенности, текущий уровень
161
11 В этом случае объем требуемой памяти равен как минимум O(m(log n + X) + nI) вместо O(n(log n + I + X)), где n — число вершин, m — число дуг, I — размер идентификатора вершины, X — размер идентификатора стимула. 162
неопределенности изменяется, структуры данных освобождаются, алгоритм работает так, как если бы новое состояние было начальным. Поскольку граф конечен, детерминирован и сильно-связен, любая его информационная проекция является конечной, детерминированной и сильно-связной, поэтому внутри любого уровня неопределенности, за исключением последнего, можно гарантированно достичь вершину, в которой допустим стимул, такой что операция call обязательно выберет дугу, выводящую из этого уровня неопределенности. Такие вершина и стимул существуют, поскольку граф является открытым. Таким образом, за конечное число шагов алгоритм достигнет последнего уровня неопределенности, состоящего из полностью определенных вершин исходного графа. Обход которого завершит уточняющий обход исходного графа. ■
6. Простое расширение технологии UniTESK Рассмотрим как можно расширить технологию тестирования UniTESK средствами тестирования в условиях неполной информации на примере инструмента разработки тестов CTesK [9]. В своих предложениях по расширению мы руководствовались тем соображением, что трудоемкость расширения должна быть минимальной.
6.1. Инструмент разработки тестов CTesK Инструмент CTesK является реализацией концепции UniTESK для языка программирования C. Для разработки компонентов тестовой системы в нем используется язык SeC (specification extension of C), являющийся расширением ANSI C. Инструмент CTesK включает в себя транслятор из языка SeC в C, библиотеку поддержки тестовой системы, библиотеку спецификационных типов и генераторы отчетов. Для пользователей Windows имеется модуль интеграции в среду разработки Microsoft Visual Studio 6.0. Компоненты тестовой системы UniTESK реализуются в инструменте CTesK с помощью специальных функций языка SeC, к которым относятся:
спецификационные функции — содержат спецификацию поведения целевой системы в ответ на единичное тестовое воздействие, а также определение структуры тестового покрытия;
медиаторные функции — связывают спецификационные функции с тестовыми воздействиями на целевую систему;
функция вычисления обобщенного состояния — вычисляет обобщенной конечно-автоматной модели целевой системы;
сценарные функции — описывают набор тестовых воздействий для каждого достижимого обобщенного состояния.
состояние
6.2. Простое расширение инструмента CTesK Для описания предикатов, которые из-за неопределенности состояния целевой системы могут принимать неопределенное значение, предлагается добавить в библиотеку CTesK
163
перечислимый тип Bool3, представляющий значения истинности в трехзначной логике, вместе с основными функциями, реализующими отрицание, дизъюнкцию и конъюнкцию: typedef enum { True_Bool3 = 1, // истина False_Bool3 = 0, // ложь Unknown_Bool3 = -1 // неопределенное значение } Bool3; // отрицание Bool3 not_Bool3(Bool3 arg); // дизъюнкция Bool3 or_Bool3(Bool3 lhs, Bool3 rhs); // конъюнкция Bool3 and_Bool3(Bool3 lhs, Bool3 rhs);
Для удобства работы со значениями типа Bool3 также в библиотеку CTesK можно добавить модальные функции возможности и необходимости: // модальная функция возможности bool may_Bool3(Bool3 arg); // модальная функция необходимости bool shall_Bool3(Bool3 arg);
Для задания отношения уточнения на множестве значений спецификационных типов предлагается добавить в спецификационные типы поле .refines, которое можно инициализировать функцией вида: bool refines(Object *lhs, Object *rhs);
Функция возвращает значение true тогда и только тогда, когда спецификационный объект rhs уточняет спецификационный объект lhs. Используя функцию refines(), обходчики в процессе построения тестовой последовательности могут учитывать изменения уровня неопределенности, тем самым подавая вспомогательные инициализирующие тестовые воздействия, которые сейчас приходится писать вручную. Поскольку значение, возвращемое постусловием спецификационной функции, имеет двузначный логический тип, а определить правильность или ошибочность поведения целевой системы в условиях неполной информации не всегда возможно, предлагается при определении структуры тестового покрытия выделять специальные ветви функциональности Unknown: { “Описание ветви функциональности”, Unknown };
Выделенные ветви функциональности описывают ситуации, в которых оракул тестовой системы не обладает достаточной информацией, чтобы вынести однозначный вердикт о правильности или ошибочности поведения целевой системы. При попадании в одну из таких ветвей можно не проверять постусловие. Так как ветви Unknown носят
164
вспомогательный характер, информация об их покрытии не должна попадать в отчеты о достигнутом покрытии. Если после выполнения теста некоторые ветви функциональности, отличные от Unknown, оказались непокрытыми, это может быть связано со сценарной неполной
требований. Возможно, что требования не определяют как достичь или идентифицировать соответствующие тестовые ситуации. Предположим, что оракул в качестве вердикта может возвращать неопределенное значение. Если после выполнения теста для некоторых ветвей функциональности вердикт оказывается неопределенным, это может быть связано с контрактной неполнотой требований. Цель тестирования — покрыть все ветви функциональности, вынеся при этом определенные вердикты, и достижение этой цели как правило связано с уточнением требований к целевой системе.
7. Заключение
[9] [10] [11] [12] [13] [14] [15] [16]
В работе были рассмотрены вопросы функционального тестирования программных систем в условиях неполной информации. На базе технологии тестирования UniTESK предложен подход к разработке функциональных спецификаций и генерации функциональных тестов, основанный на использовании неопределенных значений для моделирования состояния целевой системы, а также трехзначной логики Клини для работы с неполными требованиями и описания свойств системы. Идеи предложенного подхода нашли свое применение в проекте по разработке открытого тестого набора OLVER для операционной системы Linux.
http://www.unitesk.com/products/ctesk — страница инструмента разработки тестов CTesK. http://www.ispras.ru — сайт Института системного программирования РАН. I. Bourdonov, A. Kossatchev, A. Petrenko, and D. Galter. KVEST: Automated Generation of Test Suites from Formal Specifications. FM'99: Formal Methods. LNCS 1708, Springer-Verlag, 1999, pp. 608-621. The RAISE Language Group. The RAISE Specification Language. Prentice-Hall BCS Practitioner Series. Prentice-Hall, Inc., 1993. А. В. Хорошилов. Спецификация и тестирование систем с асинхронным интерфейсом. Институт системного программирования РАН, Препринт 12, 2006. A. Tarski. Introduction to Logic and to the Methodology of Deductive Sciences. New York, 1941. (А. Тарский. Введение в логику и методологию дедуктивных наук. М.: ГИИЛ, 1948.) Я. А. Слинин. Современная модальная логика. Развитие теории алетических модальностей (1920 – 1960 гг.). Издательство Ленинградского университета, 1976. И. Б. Бурдонов, А. С. Косачев, В. В. Кулямин. Неизбыточные алгоритмы обхода ориентированных графов. Детерминированный случай. Программирование. т. 29, № 5, стр. 245–258, 2003.
Литература [1]
[2] [3] [4]
[5] [6] [7] [8]
D. Dibois, H. Prade. Théorie des possibilités. Applications à la representation des connaissances en informatique. MASSON, Paris, Milan, Barselone, Mexico, 1988 (Дюбуа Д., Прад А. Теория возможностей. Приложения к представлению знаний в информатике: Пер. с фр. — М.: Радио и связь, 1990.) Ю. П. Пытьев. Возможность. Элементы теории и применения. М.: Эдиториал УРСС, 2000. http://www.unitesk.com — сайт, посвященный технологии тестирования UniTESK и реализующим ее инструментам. А. В. Баранцев, И. Б. Бурдонов, А. В. Демаков, С. В. Зеленов, А. С. Косачев, В. В. Кулямин, В. А. Омельченко, Н. В. Пакулин, А. К. Петренко, А. В. Хорошилов. Подход UniTesK к разработке тестов: достижения и перспективы. (Опубликовано на http://www.citforum.ru/SE/testing/unitesk/.) В. В. Кулямин, А. К. Петренко, А. С. Косачев, И. Б. Бурдонов. Подход UniTESK к разработке тестов. Программирование, 29(6): 25–43, 2003. А. Петренко, Е. Бритвина, С. Грошев, А. Монахов, О. Петренко. Тестирование на основе моделей. Открытые системы, №09/2003. (Опубликовано на http://citforum.ru/SE/testing/model/.) M. Fitting. Kleene's three-valued logics and their children. Fundamenta Informaticae, 20, 113-131, 1994. http://www.linuxtesting.org — сайт Центра верификации операционной системы Linux.
165
166
тестирования. Таким образом, граф может не только задаваться в виде статической модели тестируемой системы, но и строиться динамически, в результате наблюдений за поведением тестируемой системы во время тестирования.
Сравнение эффективности обходчиков UniTESK
Извлечение модели тестируемой системы в процессе выполнения теста позволяет добиться существенного улучшения качества тестирования, масштабируемости тестового набора и упрощения переиспользования тестовых сценариев. Для обеспечения этих преимуществ все используемые алгоритмы работы с графом должны учитывать тот факт, что информация о графе появляется только во время выполнения теста.
А. Р. Арутюнян Российско-Армянский (Славянский) государственный университет, ул. Овсепа Эмина, 123, Ереван, Армения E-mail:
[email protected]
Аннотация В данной работе исследуется эффективность генерации тестов на основе автоматического построения обхода графа, т.е. маршрута, проходящего через все его дуги. Приводятся экспериментальные данные о работе на графах различных типов обходчиков UniTESK, использующих разные алгоритмы построения обхода — алгоритм поиска в глубину и жадный алгоритм. Жадный алгоритм в большинстве случаев строит более короткие маршруты, позволяя выполнять соответствующие тесты значительно быстрее.
1. Введение В настоящее время тестирование на основе моделей получает все большее распространение. Используемые в его ходе модели могут быть более абстрактными, чем реализация, но в то же время они хорошо отражают основные особенности тестируемой системы. Относительно небольшой размер моделей позволяет реализовать их автоматическую обработку, а подобие модели тестируемой системе гарантирует, что тесты, сгенерированные на основе модели позволят провести ее систематическое тестирование. Кроме того, тесты, построенные на основе моделей, легче сопровождать и проще переиспользовать из-за их более высокого уровня абстракции и независимости от реализации. Одним из наиболее успешных примеров применения подхода тестирования на основе моделей является технология UniTESK [1,2], разработанная в Институте Системного Программирования РАН. В рамках этой технологии математические модели используются для решения основных задач тестирования: оценки корректности поведения целевой системы, генерации тестовых данных, тестовых последовательностей и оценки качества тестирования.
Основными понятиями технологии UniTESK являются понятия спецификации, медиатора, тестового сценария и обходчика. Спецификация представляет собой описание формальной модели тестируемой функции. Медиатор осуществляет взаимодействие спецификации (формальной модели) и реализации тестируемой функции. Тестовый сценарий представляет собой неизбыточное описание графа, моделирующего тестовую систему, и состоит из набора сценарных функций и функции вычисления текущего состояния — вершины графа. Каждая сценарная функция представляет совокупность однотипных тестовых воздействий. Обходчик, основываясь на тестовом сценарии, осуществляет перебор всех допустимых тестовых воздействий во всех достижимых состояниях задаваемого этим сценарием графа. Целью данной работы исследование эффективности алгоритмов обходчиков, входящих в инструмент CTesK, результаты которого позволят дат рекомендации по построению тестовых сценариев в условиях ограниченных ресурсов.
2. Сфера применения тестирования на основе спецификаций Тестирование на основе спецификаций максимально эффективно при тестировании групп функций с объемным, логически связным текстуальным описанием, а также функций с закрытым кодом. Рассмотрим преимущества подхода тестирования на основе спецификаций, на примере технологии UniTESK.
Генерация тестовой последовательности в технологии UniTESK производится на основе построения обхода графа, выступающего в качестве абстрактной модели тестируемой системы и отражающего выделенные разработчиком тестов аспекты ее поведения. Уникальной особенностью технологии UniTESK является задание графа модели в неизбыточном виде. Это означает, что перед началом тестирования о графе ничего не известно и вся информация о нем появляется только в процессе
167
168
Абстрагирование от архитектуры. Независимость тестов от внутренней архитектуры реализации становится возможной, благодаря отделению кода спецификации от медиаторов. В результате спецификации обладают самостоятельной ценностью. При переходе от одной архитектуры реализации к другой может потребоваться
изменение медиаторов, логика же тестирования остается неизменной. Разработчику тестов для системы, работающей на специфической платформе достаточно иметь спецификации и интерфейс реализации: они однозначно определяют построение медиаторной части.
Конфигурационная система. Благодаря разделению спецификаций и реализационно-зависимых компонентов теста, возникает возможность создания гибкой конфигурационной системы, способной адекватно отражать в спецификациях неописанные варианты поведения функций. В частности, это относится к тем случаям, когда поведение тестируемой функции сильно зависит от специфики конкретной реализации (implementation-defined).
Генерация отчета по покрытию проверенных требований работы функции. Такие отчеты, помимо оценки качества тестирования, дают возможность выделить часто используемые части программы, для их возможной дальнейшей оптимизации.
Разделение сценариев и спецификаций. Такое разделение позволяет использовать одно описание функциональности тестируемой системы при ее тестировании в различных условиях. Например, сценарии легко дорабатываются в следующих направлениях. o Увеличение множества тестовых значений параметров функции. Это приводит к более полному тестированию функции. Обычно для минимальной полноты тестов требуется по одному набору значений параметров на каждую ветвь функциональности, определенную в спецификации данной функции. o Вовлечение в тестирование функции связанных с ней функций. Этот метод также приводит к более полному тестированию реализации функции. Особенно он важен при тестировании функций со скрытым состоянием, на которое можно влиять исключительно опосредованно, путем вызова других функций.
Может показаться, что для разработки хорошего теста достаточно указать набор сценарных функций и определить функцию вычисления состояния, и после этого обходчик все сделает сам. В общем случае это неверно. Неправильно выбранный метод обхода, неправильные ограничения на количество состояний, неправильное определение состояния графа: все это может привести к невозможности получения результата в условиях ограниченности ресурсов. Возникает следующая проблема: ошибка может проявиться в последовательности тестовых воздействий, состоящей из сотни переходов по дугам графа, но выбранный механизм обхода будет не в состоянии обеспечить достижения подобной ситуации, по причине того, что он неудачно использует полный перебор. Пример. Если человек находится на улице у дома номер 1, и хочет дойти к дому номер 91, то он может пойти прямо и дойти до цели. Действуя иначе, он может поворачивать на любом перекрестке, стараясь не проходить по два раза по одному и тому же кварталу. По дороге он увидит много интересного, но к цели придет с колоссальным запозданием. Из этого следует, что строить обход графа, заданного в неизбыточном виде, нужно аккуратно. При неосмотрительном использовании перебора всех возможных вариантов в сложных ситуациях могут возникнуть трудности. Поэтому детали реализации алгоритма обходчика имеют большое значение для эффективности тестирования больших систем. Очевидно, что построить обход всех дуг графа можно различными путями, необязательно эквивалентными по эффективности использования памяти и времени. Приведем несколько примеров. «Цепной» пример. Описываемая в этом примере ситуация возникает в тех случаях, когда состоянием системы является целое число, и доступны два вида тестовых воздействий: увеличивающее и уменьшающее значение состояния на единицу.
3. Как эффективность обходчика влияет на тестирование Используемый в тестах, построенных по технологии UniTESK, обходчик позволяет осуществить полный перебор возможных тестовых ситуаций путем вызова всевозможных тестовых воздействий во всех состояниях графа, заданного в неизбыточном виде. Таким образом могут быть получены последовательности тестовых воздействий, которые практически никогда не проверяются при ручном построении тестов.
169
Рисунок 1. Граф состояний и переходов для «цепного» примера.
В этом случае возможен обход вида (перечисляются состояния в порядке их посещения) 0, 1, 0, 1, 2, 1, 0, …, 0, 1, 2, …, (n-2), (n-1), (n-2), …, 1, 0. Также
170
возможен
обход
вида
0,
1,
2,
…,
(n-2),
(n-1),
(n-2),
…,
1,
0.
В обоих случаях проходятся все дуги во всех состояниях графа, однако в первом случае количество тестовых воздействий проделанных при обходе равно 2n(n+1)/2 = n(n+1), тогда как во втором оно составляет только 2n.
В POSIX есть операции создания нового потока (обозначим ее как C от create),
Из приведенного примера, вытекает следующая рекомендация по работе обходчика: в случае наличия непройденных дуг из текущего состояния графа, обходчику рекомендуется первой вызывать операцию, соответствующую одной из этих непройденных дуг. То есть, не рекомендуется переходить к вызову сценарной функции B, пока не исчерпаны возможные вызовы сценарной функции A.
(K, kill). В нашей простой модели потоку разрешается иметь не более двух обработчиков завершения.
При этом порядок задания сценарных функций, определяющий последовательность их вызова обходчиком, должен тщательно обдумываться разработчиком тестов для определения максимально эффективного обхода. Примеры с потоками. Приведенные ниже примеры выбраны из-за высокой степени связности графов, а также относительно малого количества состояний и дуг, что позволяет понять существо проблемы, не рассматривая сложные случаи. О том, что такое поток (thread) и обработчик завершения потока (cleanup handler) можно прочесть в [3] и [4] соответственно. Простой пример с потоками. Граф состояний и переходов простой модели системы управления потоками, описываемой в стандарте POSIX [5], изображен на Рисунке 2. В этой модели разрешается иметь не более одного потока одновременно.
помещения функции в стек обработчиков завершения данного потока (U, от push) и выталкивания ее из этого стека (O, pop), а также операция уничтожения данного потока
В рассматриваемом случае, даже если обходчик следует рекомендации по использованию сценарных функций, существует, по крайней мере, два возможных маршрута обхода с разной эффективностью. Первый вариант (13 переходов): [0, 0], [1, 0], [1, 1], [1, 2], [1, 1], [1, 0], [0, 0], [1, 0], [1, 1], [0, 0], [1, 0], [1, 1], [2, 1], [0, 0]. Он соответствует последовательности использования сценарных функций CUOK. Обходчик пытается в каждом состоянии вызывать функции в указанном порядке. Второй вариант (15 переходов): [0, 0], [1, 0], [0, 0], [1, 0], [1, 1], [0, 0], [1, 0], [1, 1], [1, 0], [1, 1], [1, 2], [0, 0], [1, 0], [1, 1], [1, 2], [1, 1]. Он соответствует последовательности сценарных функций CKUO. Сложный пример с потоками. Рассмотрим более сложную модель той же системы, допуская уже два активных потока.
Рисунок 2. Граф состояний и переходов для простого примера работы с потоками.
Рисунок 3. Граф состояний и переходов более сложной модели потоков.
Здесь первая цифра в состоянии обозначает количество активных потоков в системе, а вторая — количество зарегистрированных обработчиков завершения потоков.
В данном случае первая цифра в состоянии обозначает количество активных потоков. Далее идут несколько цифр, отражающих количество обработчиков завершения для соответствующих потоков.
171
172
В системе имеются те же операции, что и раньше, но на этот раз разрешим потоку иметь не более одного обработчика завершения. В данном случае, даже если рекомендация по последовательному использованию сценариев выполняется, существует, по крайней мере, два возможных пути, с разной эффективностью. Первый вариант (25 переходов, соответствует порядку CKUO): [0], [1, 0], [2, 0, 0], [1, 0], [0], [1, 0], [1, 1], [2, 1, 0], [1, 1], [0], [1, 0], [2, 0, 0], [2, 1, 0], [2, 1, 1], [1, 1], [2, 1, 0], [2, 0, 0], [2, 0, 1], [1, 0], [2, 0, 0], [2, 0, 1], [2, 1, 1], [2, 1, 0], [2, 1, 1], [2, 0, 1], [2, 0, 0]. Второй вариант (27 переходов, соответствует порядку OKUC): [0], [1, 0], [0], [1, 0], [1, 1], [1, 0], [2, 0, 0], [1, 0], [1, 1], [0], [1, 0], [2, 0, 0], [2, 1, 0], [2, 0, 0], [2, 0, 1], [2, 0, 0], [2, 1, 0], [1, 1], [2, 1, 0], [1, 1], [2, 1, 0], [2, 1, 1], [2, 1, 0], [2, 1, 1], [2, 0, 1], [1, 0], [2, 0, 0], [2, 0, 1], [2, 1, 1], [1, 1].
неаккуратного абстрагирования в графе сценария от деталей реализации. В то же время, в большинстве случаев только небольшая часть дуг графа соответствует недетерминированным переходам, и существует детерминированный остовный подграф, который мог бы быть использован для построения обхода графа. Задача построения алгоритмов строящих обход недетерминированных графов, заданных неизбыточным образом, была успешно решена [10]. Но предложенный в работе [10] алгоритм является полновесным средством работы с существенно недетерминированными графами. В то же время, в большинстве случаев можно обойтись более простым решением, обладающим более узкой областью применимости, которое реализовано во втором обходчике CTesK, ndfsm [11].
Разница достигается исключительно за счет изменения расположения сценариев в обойме обходчика.
ndfsm реализует жадный алгоритм построения обхода дуг графа, опираясь при этом на предположение о наличии детерминированного полного остовного подграфа, т.е. детерминированного подграфа, в который входят все вершины исходного графа. Жадный алгоритм, оказавшись в вершине, где все дуги уже пройдены, пытается найти ближайшую к ней вершину, где еще есть непройденная дуга, и попасть в нее. Значит, ndfsm действует в соответствии с высказанной рекомендацией. Наличие детерминированного остова позволяет обходчику всегда находить путь, ведущий в нужную вершину.
Это указывает на необходимость анализа работы обходчика и применения результатов этого анализа при разработке модели, а также тестовых сценариев.
5. Сравнение эффективности обходчиков UniTESK
Следствие из рассмотренных примеров. Разница в эффективности, показанная в данных примерах невелика, что обусловлено простотой модели (малое количество состояний и дуг), а также оптимальностью последовательного использования сценариев.
4. Краткое описание обходчиков UniTESK В настоящий момент CTesK [6], один из инструментов, поддерживающих разработку тестов по технологии UniTESK, включает два обходчика, реализующих различные алгоритмы построения обхода графов и имеющих различные ограничения на вид графов, с которыми они могут работать. Первый обходчик, dfsm [7,8], реализует алгоритм генерации тестовой последовательности по неизбыточному описанию графа сценария при помощи построения его обхода в глубину и предназначен только для работы с детерминированными графами. Обход в глубину означает, что обходчик хранит цепочку из тех вершин, не все дуги которых пройдены. Попав в одну из таких вершин, он каждый раз доходит до конца цепочки, прежде чем пройти по еще не пройденной дуге. Тем самым, обходчик dfsm не следует рекомендации, сформулированной выше. Для вершин, где все дуги пройдены, этот обходчик хранит одну дугу, позволяющую (быть может, после прохождения еще каких-то дуг) попасть на цепочку вершин, имеющих еще не пройденные дуги. Этот алгоритм прекрасно проявил себя в многочисленных проектах [9] по тестированию различных видов программного обеспечения при помощи технологии UniTESK. Однако требование детерминированности графа сценария требует от разработчиков тестов дополнительных усилий, направленных на избавление от недетерминизма графа, динамически извлекаемого из наблюдений за поведением тестируемой системы. Причем такой недетерминизм появляется достаточно часто, и не только из-за недетерминированности самой тестируемой системы, но и из-за 173
В данном разделе приводятся экспериментальные эффективности двух обходчиков CTesK.
данные
о
сравнительной
В первой его части исследовалась зависимость производительности обходчиков от порядка обращений к сценарным функциям для небольших графов. В следующих частях сравнивается эффективность работы обходчиков на разнообразных графах. Сравнивалось, в основном количество проходов по дугам графа (или же обращений к сценарным функциям), выполняемых обходчиками при построении обхода. При этом выполнение каждой отдельной сценарной функции максимально облегчено, в его ходе производился минимум действий. Иногда для наглядности приведено общее время работы теста, которое измерялось в секундах с допустимой погрешностью в 1 секунду. При сравнении использовался компьютер с процессором AMD 3200+, 1024 MB памяти.
5.1. Зависимость работы обходчика от порядка сценарных функций В Таблице 1 показано количество переходов, выполняемое обходчиками для построения обхода графов из раздела 3 при различных упорядочениях сценарных функций.
174
C в этой таблице обозначает операцию создания нового потока, K — уничтожение одного из имеющихся потоков, U — помещение функции в стек обработчиков завершения потока, O — выталкивание функции из этого стека.
Порядок Простой пример (Рис. 2) сценарн Количество состояний — 4. ых Количество дуг — 8. функций ndfsm dfsm 161 22 CKUO 16 22 CKOU 16 25 CUKO 132 25 CUOK 16 19 COKU 13 25 COUK 16 22 KCUO 16 22 KCOU 16 22 KUCO 16 22 KUOC 16 22 KOCU 16 22 KOUC 16 25 UCKO 13 25 UCOK 16 25 UKCO 16 25 UKOC 13 25 UOCK 16 25 UOKC 16 19 OCKU 13 25 OCUK 16 19 OKCU 16 19 OKUC 13 25 OUCK 16 25 OUKC
Сложный пример (Рис. 3) Количество состояний — 7. Количество дуг — 13. ndfsm 31 29 29 27 30 27 31 29 30 30 31 31 29 27 29 29 27 31 30 27 30 31 27 31
На рассмотренных примерах ndfsm демонстрирует более высокую производительность. Далее мы рассмотрим дополнительные примеры, подтверждающие это.
5.2. Обход дерева Сравним эффективность обходчиков при обходе полного бинарного дерева высотой N, с операциями «опуститься на уровень ниже влево», «опуститься на уровень ниже вправо» и «подняться на уровень выше».
dfsm 76 76 76 88 88 88 68 68 60 60 68 59 64 69 60 60 69 69 88 88 61 55 68 68
Теоретическая оценка длины обхода снизу равна количеству дуг 2 N 2 4 . Эта оценка может быть достигнута, например, при обходе в симметричном порядке [12]. В Таблице 2 приведены длины обходов и время их выполнения для разных обходчиков.
N 9 10 11 12
dfsm, длина 4088 8184 16376 32760
dfsm, время 6 25 100 427
ndfsm, длина 2044 4092 8188 16380
ndfsm, время 3 12 45 190
мин. длина 2044 4092 8188 16380
Таблица 2. Длина и время обхода дерева для разных N.
Обход дерева любопытен тем, что на нем при использовании обходчика ndfsm достигается минимальная длина обхода. Обходчик dfsm делает ровно в два раза больше проходов по дугам.
5.3. Обход полного графа и его модификаций В данном разделе будут рассмотрены экспериментальные данные по построению обхода для следующих видов графов.
Таблица 1. Длина строящегося обхода в зависимости от порядка сценарных функций.
Таблица 1 показывает, что, меняя только порядок обращений к сценарным функциям, можно добиться следующего ускорения более 35% для dfsm (сложный пример, сравнение CUOK и OKUC) и более 10% для ndfsm (сложный пример, CKUO и CUOK).
Полный ориентированный граф с N вершинами, обозначаемый KN.
Граф KM(KN), представляющий собой соединенные с помощью KM M полных графов KN. Вершины этих M графов пронумерованы числами от 0 до (N-1) и все вершины с номером 0 соединены между собой полным графом.
Граф KMKN, являющийся декартовым произведением полных графов. Он тоже представляется как M графов KN с пронумерованными вершинами, в которых в сем вершинам с одинаковыми номерами соединены друг с другом.
В следующих таблицах приведены длины и время (в секундах) построения обхода разными обходчиками для таких графов. Числа N и M выбраны нечетными, потому что для полного графа с нечетным количеством вершин существует эйлеров цикл.
1
2
Соответствует второму варианту из второго примера в разделе 3. Соответствует первому варианту из второго примера в разделе 3. 175
176
Теоретическая оценка E ( K N ) N ( N 1) .
Граф K3 K5 K7 K51 K53 K55 K57 K59 K61
dfsm, длина 14 60 154 46750 52364 58410 64904 71862 79300
длины
dfsm, время 0 0 0 9 11 12 13 15 17
обхода
KN
снизу
ndfsm, длина 8 24 48 2600 2808 3024 3248 3480 3720
равна
количеству
ndfsm, время 0 0 0 1 1 1 1 1 1
его
дуг
мин. длина 6 20 42 2550 2756 2970 3192 3422 3660
Оценка длины обхода графа KMKN снизу также равна количеству его дуг E ( K M K N ) M E ( K N ) N E ( K M ) M N ( N M 2) . Граф K3K51 K5K51 K7K51 K3K53 K5K53 K7K53 K3K55 K5K55 K7K55
dfsm, длина
dfsm, время
ndfsm, длина
269265 804614 1694413 301095 898884 1890675 335337 1000234 2101501
62 186 406 68 216 302 50 164 365
8108 14024 20348 8744 15104 21888 9404 16224 23484
ndfsm, время 1 5 5 2 4 5 2 3 5
мин. длина 7956 13770 19992 8576 14840 21520 9240 15950 23100
Таблица 5. Длина и время обходов графов KMKN.
Таблица 3. Длина и время обходов полных графов.
Отметим тот факт, что обходчик ndfsm проходит на (N-1) дуг больше, чем нужно по минимуму для обхода полного графа KN. Оценка длины обхода графа KM(KN) снизу тоже равна количеству его дуг E ( K M ( K N )) M E ( K N ) E ( K M ) M N ( N 1) M ( M 1) .
Граф K3(K51) K5(K51) K7(K51) K3(K53) K5(K53) K7(K53) K3(K55) K5(K55) K7(K55)
dfsm, длина dfsm, время 140264 29 233810 53 327404 80 157106 35 261880 60 366702 94 175244 40 292110 71 409024 108
ndfsm, длина 7810 13028 18254 8434 14068 19710 9082 15148 21222
ndfsm, время 2 3 5 2 4 6 2 4 6
мин. длина 7656 12770 17892 8264 13800 19334 8916 14870 20832
Таблица 4. Длина и время обходов графов KM(KN).
Отметим, что длина обхода этих графов с помощью dfsm может быть вычислена по той же формуле, что и количество дуг: если D(G) обозначает длину обхода графа G с помощью dfsm, то D( K M ( K N )) M D( K N ) D ( K M ) . Можно предположить, что при обходе графа, состоящего из нескольких слабо связанных (при помощи одной-двух дуг) частей, длина его обхода получается сложением длин обходов частей и количества проходов по связывающим дугам.
177
Теперь длина обхода с помощью dfsm уже не может быть определена с помощью аналогичной формулы. Приведенные таблицы показывают, что на многих графах обходчик ndfsm работает эффективнее dfsm, и, если проверка детерминизма графа не является необходимым элементом тестирования, предпочтительнее использовать первый обходчик. Можно заметить, что при увеличении количества дуг длина обхода с помощью dfsm составляет порядка 50% от произведения количества дуг на количество состояний, что означает практическую невозможность использования dfsm-обходчика в случае насыщенных графов с сотнями состояний.
6. Заключение Моделирование поведения практически важной системы в виде конечного автомата часто приводит к большому числу состояний и переходов в итоговой модели. При тестировании такой системы с помощью технологии UniTESK выполняется автоматическое построение обхода графа состояний и переходов модели. Длина такого обхода в значительной степени зависит от используемого алгоритма обходчика. В работе было проведен сравнительный анализ эффективности построения обхода графов с помощью обходчиков dfsm и ndfsm, входящих в инструмент CTesK. Полученные результаты показывают, что для многих графов второй обходчик строит существенно более короткие обходы, и, соответственно, позволяет получать тесты, работающее значительно быстрее, но обеспечивающие те же значения тестового покрытия. Кроме того, показана возможность некоторого уменьшения времени работы теста только за счет изменения очередности задания сценарных функций. 178
Литература [1]
В. В. Кулямин, А. К. Петренко, А. С. Косачев, И. Б. Бурдонов. Подход UniTesK к разработке тестов. Программирование, т. 29, № 6, стр. 25–43, 2003. [2] А. В. Баранцев, И. Б. Бурдонов, А. В. Демаков, С. В. Зеленов, А. С. Косачев, В. В. Кулямин, В. А. Омельченко, Н. В. Пакулин, А. К. Петренко, А. В. Хорошилов. Подход UniTesK к разработке тестов: достижения и перспективы. Труды Института системного программирования РАН, т. 5, стр. 121–156, 2005. [3] http://www.opengroup.org/onlinepubs/007908799/xsh/threads.html [4] http://www.opengroup.org/onlinepubs/009695399/functions/pthread_cleanup_pop.html [5] http://www.unix.org/version3/ [6] http://www.unitesk.com/content/category/7/14/33/ [7] И. Б. Бурдонов, А. С. Косачев, В. В. Кулямин. Использование конечных автоматов для тестирования программ. Программирование, т. 26, № 2, стр. 61– 73, 2000. [8] И. Б. Бурдонов, А. С. Косачев, В. В. Кулямин. Неизбыточные алгоритмы обхода ориентированных графов: детерминированный случай. Программирование, т. 29, № 5, стр. 59–69, 2003. [9] http://www.unitesk.com/content/category/8/20/54/ [10] И. Б. Бурдонов, А. С. Косачев, В. В. Кулямин. Неизбыточные алгоритмы обхода ориентированных графов: недетерминированный случай. Программирование, т. 30, № 1, стр. 2–17, 2004. [11] А. В. Хорошилов. Отчет о научно-исследовательской работе. Алгоритм обхода недетерминированных графов, обладающих детерминированным полным остовным подграфом. Шифр РИ-19.0/002/216. [12] http://www.structur.h1.ru/derevo.htm
179