Здравствуйте, я решаю тригонометрические функции, такие как sin (x) и cos (x) с Расширения серии Тейлор
Проблема: Мои ценности не ошибаются, просто не очень точны
Мой вопрос заключается в том, могу ли я повысить точность этих функций, я думаю, что перепробовал все, но мне нужны ваши предложения.
double trig::funcsin(int value)
{
sum = 0;
//summation
factorial fac;
for(int i = 0; i < 7; i++)
{
sum += pow((-1), i)*(((double)pow(value, (double)2*i+1)/(double)fac.fact((double)2*i+ 1)));
}
return sum;
}
double trig::funccos(int value)
{
factorial fac;
sum = 0;
for(int i = 0;i < 7;i++)
{
sum += (pow((-1), i)*((double)pow(value, (double)2*i)/(double)fac.fact((double)2*i)));
}
return sum;
}
Пример:
Реальный: -0.7568024953
Шахта: -0,73207
Реальный: -0.27941549819
Шахта: -0,501801
Поскольку x становится больше, выходные значения становятся менее точными с экспоненциальной скоростью.
Я на компиляторе GCC, пожалуйста, дайте мне предложения
Следующий код демонстрирует ряд Тейлора (около x == 0) для функции sin ().
Как вы знаете, функция синуса повторяет идентичный цикл для каждого интервала 2 * пи.
Но ряд Тейлора — это просто многочлен — ему нужно много терминов для аппроксимации волнистой функции, такой как синус. И попытка аппроксимировать синусоидальную функцию в некоторой точке далеко от начала координат потребует столько терминов, что накопленные ошибки приведут к неудовлетворительному результату.
Чтобы избежать этой проблемы, моя функция начинается с переназначения x в диапазон одного цикла с центром в нуле, между -pi и + pi.
Лучше избегать использования функций pow и factorial, если вместо этого вы можете дешево обновлять компоненты на каждом этапе суммирования. Например, я сохраняю текущее значение для pow (x, 2 * n + 1): сначала оно устанавливается равным x (при n == 0), затем каждый раз, когда n увеличивается, я умножаю это на x * x. Таким образом, для обновления этого значения на каждом шаге требуется всего лишь одно умножение. Аналогичная оптимизация используется для факториального термина.
Эта серия чередуется между положительными и отрицательными терминами, поэтому, чтобы избежать необходимости отслеживать, нужно ли нам добавлять или вычитать термин, цикл обрабатывает два термина на каждой итерации — он добавляет первый и вычитает второй.
Каждый раз, когда вычисляется новая сумма, она сравнивается с предыдущей суммой. Если они равны (что указывает на то, что обновления превысили точность переменной sum), функция вернется. Это не лучший способ проверки завершающего условия, но он упрощает функцию.
#include <iostream>
#include <iomanip>
double mod_pi(double x) {
static const double two_pi = 3.14159265358979 * 2;
const int q = static_cast<int>(x / two_pi + 0.5);
return x - two_pi * q;
}
double func_sin(double x) {
x = mod_pi(x);
double sum = 0;
double a = 1; // 2*n+1 [1, 3, 5, 7, ...]
double b = x; // x^a
double c = 1; // (2*n+1)!
const double x_sq = x * x;
for(;;) {
const double tp = b / c;
// update for negative term
c *= (a+1) * (a+2);
a += 2;
b *= x_sq;
const double tn = b / c;
const double ns = tp - tn + sum;
if(ns == sum) return ns;
sum = ns;
// update for positive term (at top of loop)
c *= (a+1) * (a+2);
a += 2;
b *= x_sq;
}
}
int main() {
const double y = func_sin(-0.858407346398077);
std::cout << std::setprecision(13) << y << std::endl;
}