Задание №3 к курсовому проекту

Вещественный тип. Приближенные вычисления.

Составить программу на Си, которая печатает таблицу значений элементарной функции, вычисленной двумя способами: по формуле Тейлора и с помощью встроенных функций языка программирования. В качестве аргументов таблицы взять точки разбиения отрезка [a, b] на n равных частей (n + 1 точка, включая концы отрезка), находящихся в рекомендованной области хорошей точности формулы Тейлора. Вычисления по формуле Тейлора проводить по экономной в сложностном смысле схеме с точностью ε×k, где ε — машинное эпсилон аппаратно реализованного вещественного типа для данной ЭВМ, а k — экспериментально подбираемый коэффициент, обеспечивающий приемлемую сходимость. Число итераций должно ограничиваться сверху числом порядка 100. Программа должна сама определять машинное ε и обеспечивать корректные размеры генерируемой таблицы.

Дополнительное задание. Для углубленного изучения вещественных типов рекомендуется провести вычисление машинного эпсилон для других вещественных типов, а также, для других систем программирования и аппаратных средств (tcc, pcc, clang и др.) Сравните полученные результаты со встроенными константами систем программирования.

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

Дополнительное задание оформляется в виде отдельных программ. Полученные результаты необходимо включить в отчет по курсовому проекту. Успешное выполнение дополнительного задания учитывается при оценке основного.

Замечание. Формула Тейлора сводит вычисление трансцендентных функций к алгебраическим. Однако этот простой способ не применяется на практике ввиду большой ресурсоемкости и значительной погрешности. Изучение более совершенных способов вычисления значений транцендентных функций на компьютере производится в курсе численных методов.

Варианты

Если lm --- это переменная, содержащая количество знаков после запятой, то вывести число с точностью до lm можно так:

printf("%.*f\t %.3f\t  %.*f\n", lm, s(x), x, lm, 1/(2*x - 5));

Комментарий к данной работе.

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

Выявил причины неточностей Артемьев А., спасибо ему за это. Текст оформил Чечеринда С. для исправления неточностей при объяснении данной работы на лабораторных занятиях.

Работа проводится на i686 GNU/Linux.

Рассмотрим два файла верный вариант, неверный вариант. Обратите внимание на различия между ними:

diff kr3.c bad.c

7,8d6

< Float findent(Float x) { return x; }

<

12c10

< while ( findent(eps/2+1) > 1 ) eps=eps/2;

---

> while ( (eps/2+1) > 1 ) eps=eps/2;

Для наглядности приведу части кода, в которых имеются различия. Верный вариант:

Float findent(Float x) { return x; }

Float epsylone() {

Float eps=1.0;
while ( findent(eps/2+1) > 1 ) eps=eps/2; return eps;
}

Неверный вариант:

Float epsylone() {

Float eps=1.0;
while ( (eps/2+1) > 1 ) eps=eps/2; return eps;
}

Как видно, в верном варианте добавлена функция findent, которая не меняет математического смысла выражения. Но, внимание, результаты работы программ сильно различаются. Результат верного варианта:

The epsylone is 0.0000000000000002

Width is 16

Результат неверного варианта:

The epsylone is 0.0000000000000000001

Width is 19

В результате мы видим сильные различия. Хотя математически записано эквивалентные выражения.

Объясняется это тем, что реально вычисление арифметических операций проходит на регистрах процессора, размер которых, в большинстве случаев, не совпадает с размером типа. Таким образом, мы имеем --- тип double размером 64 бита, а размер регистра процессора для вещественных операций --- 80 бит (эта величина может быть разной для разных процессоров). В итоге, в неверном варианте мы, якобы, вычисляем машинное эпсилон для double длинной в 64 бита, а, на самом деле, вычисляем машинное эпсилон для 80-ти битного регистра процессора, причем даже его при сохранении в 64-битный double мы обрезаем и получаем число, которое не характеризует ни double, ни регистр процессора. То есть это ошибка. В верном варианте мы каждый раз при сравнении промежуточного значения эпсилон с единицей приводим его к типу double. Получается это за счет того эффекта, что функция, возвращая свой аргумент, кладет его в стек, то есть в 64 бита типа double.

Спасибо за внимание.