Снижение точности функций вычислительной тригонометрии и повышение процента ошибок

Здравствуйте, я решаю тригонометрические функции, такие как 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, пожалуйста, дайте мне предложения

1

Решение

Следующий код демонстрирует ряд Тейлора (около 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;
}
2

Другие решения