- •Овсянник в.Н. Лабораторные работы по курсу «Объектно-ориентированное программирование»
- •13. Разработка приложения с архитектурой «документ-вид». Часть 2 120
- •14. Разработка приложения с архитектурой «документ-вид». Часть 3 135
- •1.Разработка типового консольного приложения
- •1.1.Методические указания
- •1. Запуск и настройка ис mvs-2008.
- •2.Поиск данных в массиве
- •3.Обработка текстового файла
- •4.Задача «куча камней»
- •5.Реализация алгоритма полного перебора
- •6. Исследование погрешностей вычислений
- •7.Поиск экстремумов с ограничениями или «Брачное агенство»
- •Постановка задачи
- •Задание
- •Листинг файла mAgency.Cpp (с главной функцией)
- •Листинг файла Lib.H
- •Листинг файла Lib.Cpp
- •8.Приложение с окном вида
- •9.Разработка графического редактора
- •Задание.
- •9.1.Проектирование программы
- •9.2.Разработка интерфейса
- •Установка флагов
- •Создание меню и панели инструментов
- •Обработка сообщений о нажатии кнопки мыши
- •9.3.Рисование
- •9.4.Растягивание фигур
- •9.5.Обновление изображения
- •9.6.Сохранение метафайла на диске и его загрузка
- •10.Приложение, основанное на модальном диалоговом окне
- •Методические указания Модальные и немодальные диалоговые окна
- •Ресурсы и элементы управления
- •Сценарий создания приложения
- •Разбор приложения
- •Усовершенствование приложения
- •11.Приложение с контекстным меню и строкой состояния
- •11.1.Методические указания к первой части работы
- •11.2.Сценарий выполнения первой части работы
- •11.3.Методические указания ко второй части работы Строка состояния
- •Определение секций в строке состояния
- •Строка сообщений
- •Индикатор состояния
- •Управление строкой состояния
- •11.4.Сценарий выполнения второй части работы
- •12.Разработка приложения с архитектурой «документ-вид». Часть 1
- •12.1.Сценарий создания приложения
- •13.Разработка приложения с архитектурой «документ-вид». Часть 2
- •13.1.Методические указания Класс коллекций cObList
- •13.2.Сценарий выполнения работы
- •14.Разработка приложения с архитектурой «документ-вид». Часть 3
- •14.1.Методические указания
- •15.Класс вектор
- •15.1.Пример класса tVector
- •15.2.Класс tVector с перегруженными операциями
- •16.Приложение с таблицей
- •16.1.Вариант 1 Сценарий выполнения работы
- •16.2.Вариант 2 Сценарий выполнения работы
6. Исследование погрешностей вычислений
Работая над решением задачи, всегда полезно знать правильный
ответ
Закон Мерфи
Цель работы – исследование вычислительных погрешностей при использовании различных вещественных типов данных и получение практических навыков при программировании сугубо вычислительных алгоритмов. (4 час.)
Методические указания.
Как известно, любые вычисления, в нетривиальных случаях, можно выполнить только с некоторой точностью. Например, результатом операции 1.0/3.0 является 0 целых и бесконечное число "троек". В то же время выполнение операторов
cout.precision(20);
cout<<1./3.;
даст следующий результат:
0.33333333333333331482
Отметим, что для констант 1. и 3. в приведенном выше выражении компилятором выбран тип double по умолчанию, в связи с чем значащих цифр в полученном результате 16, а последние 4 цифры (1482) являются случайными (или псевдослучайными, строго говоря).
Выполнение же оператора
cout<<(float)1./(float)3.;
дает такой результат
0.33333334326744079589
В этом случае явно заданы типы констант 1. и 3. посредством приведения типа, вследствие чего число значащих цифр и сокращается до 7. Еще один поучительный (или умопомрачительный?!) пример - это такой фрагмент программы:
cout.precision(20); // выводить 20 значащих цифр
cout<<0.5; // выводится 0.5
cout<<0.6; // выводится 0.59999999999999998
cout<<0.6f; // выводится 0.60000002384185791
cout<<0.5+1e-20; // выводится 0.5
Или другой пример – вывод значений от -1 до +1 с шагом 0.1 следующим образом (проверьте, что получится, но не забудьте оператор cout.precision(20);):
for(double d=-1.0; d<=1.0; d+=0.1) cout<<d<<endl;
Не правда ли, интересные результаты? Может быть, прикладная математика действительно имеет право на существование, как и специалисты в этой области?
Задание.
В данной работе требуется реализовать вычисление числа Эйлера Е в степени X по формуле:
n
EX = 1+ ∑ (Xk / k!) (1)
k=1
с максимально возможной точностью для двух вещественных типов: float и double. Как нетрудно видеть, искомый результат – E в степени X – получается путем суммирования членов ряда, причем с ростом k абсолютные значения членов ряда убывают, стремясь к нулю. Так как число значащих цифр для любого типа данных ограничено, то, начиная с некоторого k, прибавление очередного члена ряда уже не будет влиять на результат – EX. Если мы найдем такой член ряда, то по его величине мы и сможем судить о погрешности вычисления EX. Нахождение такого члена ряда для каждого из названных вещественных типов данных (float и double) и является Вашей благородной задачей, решив которую Вы прославите себя на века. Для того чтобы убедиться в корректности программы, необходимо вывести на монитор для каждого типа данных:
значение EX;
погрешность вычисления EX;
число членов ряда n, при котором достигается максимальная точность;
значение EX, полученное с помощью библиотечной функции exp().
Проверьте корректность работы программы при нулевых, положительных, отрицательных и нецелых значениях X. Вычисление EX для каждого из вещественных типов данных удобно вынести в отдельную функцию. Программу можно оформить в виде консольного приложения.
Для того чтобы с помощью потока cout выводить заданное число значащих цифр выражения вещественного типа, можно воспользоваться компонентной функцией precision() класса iostream
precision(число_значащих_цифр)
следующим образом:
cout.precision(20);
Для первоначальной проверки корректности вычислений напишите программу, которая будет вычислять EX при заданном n. Посмотрите, будет ли уточняться значение EX с ростом n.
При X==1 значение EX должно быть равно приблизительно 2.718281. Можно воспользоваться также библиотечной функцией exp(), объявленной в заголовочном файле Math.h, для сравнения результатов своих вычислений с библиотечными.
Если при вычислении какой-либо величины (вещественного типа) происходит выход за границу диапазона для данного типа, то состояние ошибки не возникает, а значение величины при выводе представляется как 1.#INF (сокращение от infinite – бесконечный). Это характерное значение можно (и нужно!) проконтролировать с помощью функции infinity(), которая в этом случае возвращает значение true. Например:
float fact;
// вычисления
if(fact==numeric_limits<float>::infinity()) cout<<"Караул, переполнение!!!"<<endl;
Для типа double надо использовать функцию таким образом:
if(fact==numeric_limits<double>::infinity())cout<<"Ужас, переполнение!!!"<<endl;
Для использования функции infinity() надо подключить заголовочный файл:
#include <limits>
Если Вам удастся разработать удачный алгоритм вычисления экспоненты, то Ваши результаты будут практически совпадать (до 15 значащих цифр) с библиотечной функцией. Например, для типа double можно получить такие результаты:
X=709.75
Exp = 1.7398368732641691e+308 eps=7.8270482774736306e+291 n=938
Math::exp = 1.7398368732641605e+308
