Реализовать вычисление числа Эйлера Е в степени X

Задание.
Реализовать вычисление числа Эйлера Е в степени X по формуле:

EX = 1+ ∑ (Xk / k!)^2 (1)
k=1

с максимально возможной точностью для двух вещественных типов: float и double. Число значащих цифр для любого типа данных ограничено, значит, начиная с некоторого 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

Забавная задачка. С разложением экспоненты в ряд Тейлора действительно наврали ))

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

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

x^k / k! == x^(k-1) * x / (k-1)! * k == [x^(k-1) / (k-1)!] * (x / k)

К тому же работает бысто и эффективно.

#include "stdafx.h"

#include <iostream>
#include <limits>
#include <math.h>

using namespace std;

template <typename Tnum>
struct ResultStruct {
    Tnum value;
    Tnum eps;
    unsigned int iteration;

    ResultStruct(Tnum v, Tnum e, unsigned int i) { value = v; eps = e; iteration = i; }
};

template <typename Tnum>
Tnum factorial(Tnum x) {
    Tnum res = 1;
    for (Tnum i = 1; i < x; i++)
        res *= i;
    return res;
}

template <typename Tnum>
ResultStruct<Tnum> ex(Tnum x) {
    Tnum res = Tnum(1);
    Tnum prev = Tnum(-1);
    Tnum fraction = Tnum(1);
    Tnum eps = Tnum(1);
    unsigned int k = 1;
    while (res != prev) {
        prev = res;
        eps = eps * (x / k);
        if (eps == numeric_limits<Tnum>::infinity()) {
            cout << "Переполнение при вычислении " << k << "-го элемента ряда" << endl;
            return ResultStruct<Tnum>(res, eps, k);
        }
        res += eps;
        if (res == numeric_limits<Tnum>::infinity()) {
            cout << "Переполнение при вычислении значения экспоненты на итерации " << k << endl;
            return ResultStruct<Tnum>(res, eps, k);
        }
        k++;
    }
    return ResultStruct<Tnum>(res, eps, k-1);
}


int _tmain(int argc, _TCHAR* argv[])
{
    setlocale(LC_ALL, "Russian");

    double arg = 709.75;

    std::cout << "Введите аргумент: ";
    std::cin >> arg;

    double mathexp = exp(double(arg));
    std::cout.precision(30);
    std::cout << "math::exp() = " << mathexp << endl << endl;

    ResultStruct<float> fres = ex(float(arg));
    std::cout << "FLOAT\n" 
        << "Exp = " << fres.value << endl
        << "      " << mathexp << endl
        << "eps = " << fres.eps << endl
        << "k = " << fres.iteration << endl << endl;

    ResultStruct<double> dres = ex(double(arg));
    std::cout << "DOUBLE\n" 
        << "Exp = " << dres.value << endl
        << "      " << mathexp << endl
        << "eps = " << dres.eps << endl
        << "k = " << dres.iteration << endl << endl;

    system("pause");
    return 0;
}

Спасибо большущее..
А можете объяснить в двух словах программу?

А проще можно реализовать, для студента 1 курса это безумно замудрено)))

Мария, рад был помочь ))

Особо замудрёного тут ничего нет. Единственно, я использовал механизм шаблонов (template), но это чисто от лени — мне было влом копипастить описание структуры и функции под два типа данных: float и double. И компилятор сделал это за меня )).

Можно убрать template'ы, а Tnum заменить в одной копии на float, а во второй на double. А описание структуры можно даже оставить общим с типом double — она используется только для возврата результата из функции и float-значения приведутся к double автоматически.

Кстати, функция factorial() здесь вообще не нужна. Осталась от экспериментов. Только что обратил внимание ))

По программе:

Функция main(), думаю, вопросов не вызывает ))

Функция ex() реализует вычисление значения экспоненты разложением в ряд Тэйлора (см. ссылку от Алана). Ф-ция возвращает структуру, содержащую значение экспоненты (поле value), последний вычисленный член ряда (eps) и количество вычисленных членов ряда (iteration).

Цикл while реализует накопление суммы ряда.

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

Переменная k — номер члена ряда или количество вычисленных членов ряда (или — количество итераций, т.е. сколько раз был выполнен цикл while).

Локальная переменная eps — это последний вычисленный член ряда. Финт с его вычислением я описал в своём первом посте. На каждом проходе цикла вычисленное значение eps прибавляется к сумме в res.

Имеются две проверки на арифметическое переполнение в тех местах, где таковое может произойти.

В операторах return создаётся временный объект (структура) для возврата результата и инициализируется вычисленными значениями. В return в самом конце функции от номера члена ряда (k) необходимо отнять единицу, поскольку прибавление этого члена уже не повлияло на конечный результат.

Вот вроде и всё.

Я не поняла, как можно убрать template, я хоть и второкурсница, но для меня тоже пока что замудрено.. Си начали только в этом году учить..

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

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

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.