Программа на С неправильно вычисляет значение ряда Тейлора для больших n
Программа должна вычислять значение формулы ниже с точностью до n-ного члена и с максимальной точностью, то есть то бесконечности (наступает момент, когда дальше считать сумму бессмысленно, именно поэтому можем посчитать до бесконечности).
Вот мой код:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
long double powl(long double x, unsigned long y) {
long double res = 1;
for (int i = 0; i < y; i++) {
res *= x;
}
return res;
}
long double move(long double x, unsigned int n) {
long double res = 1;
while (n > 0) {
res *= x / n;
n -= 1;
}
return res;
}
void teylor_na(long double x, unsigned int n) {
long double sum = 0;
for (unsigned int i = 1; i <= n; i += 1) {
sum += (powl(-1, i + 1)) * (move(x, i));
}
printf("non-accurate result is %Le\n", sum);
}
void teylor_a(long double x) {
int critical = 0;
long double sum = 0;
double p_inf = powl(10, 100000);
double n_inf = -powl(10, 100000);
for (unsigned int i = 1; ((sum + ((powl(-1, i + 1)) * (move(x, i))) != p_inf) &&
(sum + ((powl(-1, i + 1)) * (move(x, i))) != n_inf) &&
(sum + ((powl(-1, i + 1)) * (move(x, i))) != sum)); i += 1) {
sum += (powl(-1, i + 1)) * (move(x, i));
critical = i;
}
printf("accurate result is %Le\n", sum);
printf("you will get the same with non-accurate function if n = %d", critical);
}
void main() {
long double x;
unsigned int n;
printf("Enter x: ");
scanf("%Le", &x);
printf("Enter n: ");
scanf("%lu", &n);
teylor_na(x, n);
teylor_a(x);
}
При малых n функция хорошо считает ответ до n-го члена. Начиная примерно со 160 начинаются расхождения. Дан тест x = 88 n = 1000. Программу нужно исправить так, чтобы она правильно проходила его. Как вы видите, n довольно большое. Сначала программа вычисляла отдельно -1 в степени, x в степени и факториал n, но происходило переполнение. Поэтому пришлось вычислять (x^n)/(n!) отдельно в функции move. Но здесь появилось много деления, которое, как я предполагаю, дает большую погрешность и программа дает неправильный результат! Но как его убрать, я не знаю. Для большего понимания, вот что программа выдает, если каждую итерацию цикла teylor_na выводить сумму, то, что будет к ней прибавлено и номер итерации:

То есть, в конкретный момент суммы почему-то перестают чередоваться по знаку, а потом сумма и вовсе застывает на определенном значении. Почему так происходит я понять не смог
Ответы (1 шт):
Тут весь вопрос, вам нужны шашечки или вам надо ехать?
Дело в том, что этот ряд — это 1-exp(-x), так что для 88 вы получите 1-6e-39, и никакая точность double вам такой ответ просто не выдаст. Для точности где-то в 18 разрядов, которую обеспечивает double, вы можете считать до 40 и не более.
Так что если нужны "шашечки", т.е. просто сдать работу — рисуете код типа
double bruteForce(double x, unsigned int n)
{
double term = x, sum = x;
for(unsigned int i = 2; i <= n; i++)
sum += term *= -x/i;
return sum;
}
который считает ваш ряд (т.е. показываете свое умение преподу) и поясняете, почему для заданных им значений получится ерунда — из-за роста членов до примерно n = x, который "забьет" всю точность и того, что результат просто равен 1.
Если преподу надо какие-то другое шашечки, то ему надо точно пояснить, что он от вас хочет.
Если препод умный и хочет ехать, т.е. конкретный результат, то, как я же говорил, для таких чисел он в double не влезет, т.е. все, что вы можете — это дать результат в виде "1-ε, где ε = ....".
Как посчитать этот ε? С конца. Т.е. считать не сумму этих первых 1000 чисел, а начиная с 1001 и далее, пока точность не станет достаточной — для оценки этого хвоста ε. Зная точный результат суммы бесконечного ряда.
Но что-то "терзают меня смутные сомнения" (с), что это выходит за рамки обычной лабы для начинающих.
Других вариантов не вижу. Даже если собирать соседние члены в пары — все равно это не даст достаточного уменьшения членов для работы с указанными аргументами.
Впрочем, может, вы эти 88 и 1000 не из задания взяли, а сами "из головы придумали"? тогда просто не берите такие совершенно непригодные для численных расчетов через ряды значения. Универсальных методов ("серебряной пули") не существует...