Ошибка с выходом за границы переменной при выислении суммы ряда

Итак, есть задачка с рядами которые нужно считать, условие задачи на скриншоте ниже. Входные данные это X - вычисляемая точка и n- количество членов ряда которые нужно вычислить(задается пользователем) Я написал программу которая работает для небольших значений, например X=3 n = 15 работает полностью корректно, но как только я повышаю X допустим до 10 и N до 30+ то начинаются проблемы. Воспользовавшись нехитрым методом дописывания printf и вывода значений после каждой итерации я выяснил что проблема заключается в том что на определенных итерациях (в зависимости от введенных значений) факториал или числитель уходит за пределы которые может уместить float. Разумеется я менял float на double, это не исправляет ситуацию а скорее чуть смещает появление inf и некорректного результата -nan(ind) в конечном итоге. Вопрос следующий: как мне используя только Float решить эту проблему? В чем моя ошибка? Плюс ко всему буду очень благодарен человеку который опишет все недостатки кода как со стороны оптимальности, чтобы я не допускал их в будущем, заранее огромное спасибо. введите сюда описание изображения`

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


float GetFactorial(float n)
{
    if (n == 0.0 || n == 1.0)
    {
        return 1.0;
    }
    else
    {
        return n * GetFactorial(n - 1.0);
    }
}

float Degree(float a)
{
    a = ((a * 2) + 1);
    return a;
}

float Numerator(float n)
{
    n = (pow(3, 2 * n)) - 1;
    return n;
}

float ValueAtPoint(float p, float n)
{
    float TopOfDrown=0,num=0,fact=0;
    float  temp = 0;
    temp = Degree(n);
    num = (Numerator(n) * pow(p, temp)) / (GetFactorial(n*2+1));
    return num;
}


int main()
{
    setlocale(LC_ALL, "Russian");
    float Point = 0,NumOfRow = 0,Eps = 0,Res=0,temp = 0,sin=0;
     int n = 1, i = 0,counter=1;

    printf("Введите вычисляемую точку:");
    Point = GetCorrect(Point);
    printf("\nУкажите максимальное число элементов ряда:");
    NumOfRow = GetCorrect(NumOfRow);

    for (i = 1; i <= NumOfRow; i++)
    {
        temp = ValueAtPoint(Point, i);      
        if (counter % 2 == 1)
        {
            Res +=temp;
            counter += 1;
        }
        else
        {
            Res -=temp;
            counter += 1;
        }
       
    }

    printf("sin^3 от %f равен:%f",Point, (3 * Res) / 4);
    printf("\nПравильный ответ: %f", pow(sinf(Point),3));

    return 0;
}

Ответы (3 шт):

Автор решения: MBo

Ну проблемы с кодом есть, конечно.

Факториал через рекурсию считать не нужно, для этого есть простой цикл. Впрочем, здесь вообще его считать в явном виде не требуется.

pow используйте там, где без него трудно обойтись, здесь он тоже нафиг не нужен

Есть проблемы с частью числителя, но остальное легко выражается через прошлый член, при этом часть больших чисел (при x>1) сможет сократиться

f = - 0.75*x
f3 = 0.0
summ = 0.0
в цикле по i

   f3 = f3 * 9 +  8
   f = - f * x * x * f3 / ((2*i*(2*i+1))
   summ += f
→ Ссылка
Автор решения: Harry

Значит, так. Поскольку в задаче четко сказано, что это за функция, то...

...то первое, что мы делаем — приводим x к соответствующему значению в диапазоне от 0 до двух "пи". Есть такой прием :) И работаем.

float Series(float x, unsigned int N)
{
    x = fmod(x,2*3.1415925358979323846);
    float term = x*x*x;
    x *= -x;
    float sum = term, tr = 8;

    for(int n = 2; n <= N; ++n)
    {
        term /= tr;
        tr = (tr+1)*9-1;
        term *= x*tr/(2*n*(2*n+1));
        sum += term;
    }
    return sum;
}

Иначе вы никак не уберете бешеный рост членов до определенного момента.

Здесь проблемы будут при больших значениях N. Их можно избежать, если отказаться от N, и перейти к точности — при больших N ничего хорошего уже не будет... Или, начиная с какого-то значения N, изменить вычисление коэффициента 3 в степени 2N, и при вычислении очередного члена просто выполнять умножение на 9 (оценив, когда погрешность из-за вычитания 1 больше не будет играть роли).

Update

Реализация идеи Stanislav Volodarskiy о расписывании члена ряда как разности и с приведением к диапазону от 0 до пол-пи :)

float Series(float x, unsigned int N)
{
    const float pi = 3.1415925358979323846;
    int sign = x < 0 ? -1 : 1;
    x = fmod(fabs(x),2*pi);
    if (x > pi)
    {
        x -= pi;
        sign = -sign;
    }
    if (x > pi/2) x = pi - x;

    float term2 = x*x*x/8, term1 = 9*term2;
    x *= -x;

    float sum = term1 - term2;
    for(int n = 2; n <= N; ++n)
    {
        float quot = 2.*n;
        quot *= quot+1;
        term1 *= 9*x/quot;
        term2 *= x/quot;
        sum += term1-term2;
    }
    return sign*sum;
}
→ Ссылка
Автор решения: Stanislav Volodarskiy

Почему не надо считать во float?

Расчёт выполнен для x = 6.283185. В таблице приведены слагаемые (члены ряда) и ошибка их представления во float. Внизу итог. Обращу внимание что истинное значение функции ноль. А самый лучший метод вычислит 0.089656±0.782165:

итерация      слагаемое(ошибка представления)
    
      1      248.050186(0.000016)
      2    -4896.313477(0.000246)
      3    41881.378906(0.001955)
      4  -206928.656250(0.007813)
      5   668480.875000(0.031250)
      6 -1522555.500000(0.062500)
      7  2576064.750000(0.125000)
      8 -3365039.500000(0.125000)
      9  3495958.500000(0.125000)
     10 -2957462.000000(0.125000)
     11  2076686.125000(0.062500)
     12 -1229764.000000(0.062500)
     13   622424.750000(0.031250)
     14  -272353.531250(0.015625)
     15   104052.429688(0.003906)
     16   -35009.871094(0.001953)
     17    10453.115234(0.000488)
     18    -2788.327148(0.000122)
     19      668.494263(0.000031)
     20     -144.829163(0.000008)
     21       28.493151(0.000001)
     22       -5.113019(0.000000)
     23        0.840280(0.000000)
     24       -0.126937(0.000000)
     25        0.017687(0.000000)
     26       -0.002280(0.000000)
     27        0.000273(0.000000)
     28       -0.000030(0.000000)
     29        0.000003(0.000000)
     30       -0.000000(0.000000)
               ------------------
               0.089656(0.782165)
#include <math.h>
#include <stdio.h>

float err(float x) {
    x = fabsf(x);
    float y = nextafterf(x, INFINITY);
    return (y - x) / 2;
}

typedef struct {
    float x;
    float e;
} range_t;

range_t f(float x, int n) {
    float sum_err = 0;
    float term1 = -0.75f * x;
    float term2 = -0.75f * x;
    float xx = -x * x;
    float sum = 0;
    for (int i = 0; i < n; ++i) {
        float f = xx / (float)((2 * i + 2) * (2 * i + 3));
        term1 *= 9 * f;
        term2 *= f;
        printf("    %3d %15.6f(%f)\n", i + 1, term1 - term2, err(term1) + err(term2));
        sum_err += err(term1) + err(term2);
        sum += term1 - term2;
    }
    range_t r = {sum, sum_err};
    return r;
}

int main() {
    float x = 2*3.1415925358979323846f;
    range_t r = f(x, 30);
    printf("%f %f(%f) %f\n", x, r.x, r.e, powf(sinf(x), 3));
}
→ Ссылка