Некорректный вывод при попытке вычислить синус с помощью ряда Тейлора на C

Вопрос или проблема

Наш профессор дал нам задание вычислить синус заданного числа с помощью ряда Тейлора, пока два последовательных члена не различаются менее чем на 10-6, но раз я новичок в компьютерных науках, я не совсем уверен, как поступить дальше. Я полагаю, что факториал вычисляется правильно.

Я ожидал, что вывод суммы будет совпадать с выводом sin(x) из встроенной библиотеки.

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

#include <stdio.h>
#include <math.h>

int main()
{
    int val = 0, neg;
    long double sum = 0.0, x, prev = 0.0, curr, factorial = 1.0;
    printf("Введите X:\n");
    scanf("%Lf", &x);
    curr = x;
    while (fabs(curr - prev) >= 0.000001){
        val++;

        prev = curr;
        factorial *= (2 * val) * (2 * val + 1);

        curr = (pow(x, 2 * val + 1)) / factorial;

        if (val % 2 == 1)
            neg = -1;
        else
            neg = 1;
        sum += neg * curr;     
    }
    printf("ряд Тейлора sinx: %Lf\n", sum);
    printf("математическая библиотека sinx: %lf\n", sin(x));
    return 0;
}

Вы установили curr = x; и не установили sum = x; тоже.

Это очевидно, когда вы выводите операции:

#include <stdio.h>
#include <math.h>

int main(void)
{
    int val = 0, neg;
    long double sum = 0.0, x, prev = 0.0, curr, factorial = 1.0;
    printf("Введите X:\n");
    scanf("%Lf", &x);
    curr = x;
    while (fabsl(curr - prev) >= 0.000001)
    {
        val++;

        prev = curr;
        factorial *= (2 * val) * (2 * val + 1);

        curr = (powl(x, 2 * val + 1)) / factorial;

        if (val % 2 == 1)
            neg = -1;
        else
            neg = 1;
        sum += neg * curr;
        printf("val = %2d: neg = %+2d; factorial = %12.0Lf; term = %15.9Le; "
               "old = %15.9Le; new = %15.9Le\n",
               val, neg, factorial, curr, prev, sum);
    }
    printf("ряд Тейлора sinx: %Lf\n", sum);
    printf("математическая библиотека sinx:  %Lf\n", sinl(x));
    return 0;
}

что приводит к следующему:

Введите X:
1
val =  1: neg = -1; factorial =            6; term = 1.666666667e-01; old = 1.000000000e+00; new = -1.666666667e-01
val =  2: neg = +1; factorial =          120; term = 8.333333333e-03; old = 1.666666667e-01; new = -1.583333333e-01
val =  3: neg = -1; factorial =         5040; term = 1.984126984e-04; old = 8.333333333e-03; new = -1.585317460e-01
val =  4: neg = +1; factorial =       362880; term = 2.755731922e-06; old = 1.984126984e-04; new = -1.585289903e-01
val =  5: neg = -1; factorial =     39916800; term = 2.505210839e-08; old = 2.755731922e-06; new = -1.585290154e-01
val =  6: neg = +1; factorial =   6227020800; term = 1.605904384e-10; old = 2.505210839e-08; new = -1.585290152e-01
ряд Тейлора sinx: -0.158529
математическая библиотека sinx:  0.841471

Вывод первого вычисления показывает ошибку. Исправление заключается в том, чтобы установить sum (а также curr) в x:

#include <stdio.h>
#include <math.h>

int main(void)
{
    int val = 0, neg;
    long double sum = 0.0, x, prev = 0.0, curr, factorial = 1.0;
    printf("Введите X:\n");
    scanf("%Lf", &x);
    sum = curr = x;
    while (fabsl(curr - prev) >= 0.000001)
    {
        val++;

        prev = curr;
        factorial *= (2 * val) * (2 * val + 1);

        curr = (powl(x, 2 * val + 1)) / factorial;

        if (val % 2 == 1)
            neg = -1;
        else
            neg = 1;
        sum += neg * curr;
        printf("val = %2d: neg = %+2d; factorial = %12.0Lf; term = %15.9Le; "
               "old = %15.9Le; new = %15.9Le\n",
               val, neg, factorial, curr, prev, sum);
    }
    printf("ряд Тейлора sinx: %Lf\n", sum);
    printf("математическая библиотека sinx:  %Lf\n", sinl(x));
    return 0;
}

Вывод:

Введите X:
1
val =  1: neg = -1; factorial =            6; term = 1.666666667e-01; old = 1.000000000e+00; new = 8.333333333e-01
val =  2: neg = +1; factorial =          120; term = 8.333333333e-03; old = 1.666666667e-01; new = 8.416666667e-01
val =  3: neg = -1; factorial =         5040; term = 1.984126984e-04; old = 8.333333333e-03; new = 8.414682540e-01
val =  4: neg = +1; factorial =       362880; term = 2.755731922e-06; old = 1.984126984e-04; new = 8.414710097e-01
val =  5: neg = -1; factorial =     39916800; term = 2.505210839e-08; old = 2.755731922e-06; new = 8.414709846e-01
val =  6: neg = +1; factorial =   6227020800; term = 1.605904384e-10; old = 2.505210839e-08; new = 8.414709848e-01
ряд Тейлора sinx: 0.841471
математическая библиотека sinx:  0.841471

Обратите внимание, что я использовал функции long double fabsl(), powl(), sinl(), чтобы соответствовать аргументам long double (в обоих вариантах кода). Важно быть последовательным, хотя вычисление может использовать переменные и функции double и выдавать тот же вывод.

Ответ или решение

Неправильный вывод при расчете синуса с использованием ряда Тейлора на языке C

При выполнении задания вашего преподавателя по вычислению синуса заданного числа с использованием ряда Тейлора в языке программирования C возникла ошибка. В результате неправильного кода вы получаете неверный результат. В этом ответе мы разберем проблему, проанализируем код и предложим решение.

Проблема

Ваш начальный код устанавливает переменную curr равной x. Это вызывает путаницу, так как синус в ряде Тейлора рассчитывается не только от начального значения, но и от дополнительных членов ряда. Первый член ряда Тейлора для функции синуса это просто x. Без установки sum равным x, расчеты будут неверными, и вы получите результат, не соответствующий стандартной библиотечной функции sin(x).

Ваша текущая реализация могла выглядеть так:

curr = x;
while (fabs(curr - prev) >= 0.000001) {
    // ...
}

Вы не инициализируете sum этим значением x, что приводит к тому, что расчеты будут смещены. Результат выглядит как 0.158529, вместо ожидаемого 0.841471 для sin(1.0).

Решение

Чтобы исправить код, вам необходимо инициализировать как sum, так и curr значением x и следить за корректным порядком операций в вашей программе. Вот исправленный пример кода:

#include <stdio.h>
#include <math.h>

int main(void) {
    int val = 0, neg;
    long double sum = 0.0, x, prev = 0.0, curr, factorial = 1.0;
    printf("Введите X:\n");
    scanf("%Lf", &x);

    sum = curr = x;  // Инициализация sum и curr значением x

    while (fabsl(curr - prev) >= 0.000001) {
        val++;
        prev = curr;
        factorial *= (2 * val) * (2 * val + 1); // Вычисление факториала

        curr = (powl(x, 2 * val + 1)) / factorial; // Следующий член ряда

        // Определение знака текущего члена
        neg = (val % 2 == 0) ? 1 : -1;
        sum += neg * curr; // Добавление элемента к сумме

        // Для отслеживания промежуточных вычислений
        printf("val = %2d: neg = %+2d; factorial = %12.0Lf; term = %15.9Le; old = %15.9Le; new = %15.9Le\n",
               val, neg, factorial, curr, prev, sum);
    }

    printf("Синус по ряду Тейлора: %Lf\n", sum);
    printf("Синус по библиотечному sin(): %Lf\n", sinl(x));

    return 0;
}

Выводы и рекомендации

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

Синус по ряду Тейлора: 0.841471
Синус по библиотечному sin(): 0.841471

Обратите внимание на использование функций fabsl(), powl() и sinl(), которые соответствуют типу long double для обеспечения более высокой precision.

При работе с числовыми методами, такими как разложение в ряд Тейлора, важно тщательно следить за инициализацией переменных и порядком выполнения операций для избежания ошибок. Мы надеемся, что предоставленные рекомендации помогут вам успешно завершить задание.

Оцените материал
Добавить комментарий

Капча загружается...