Нет идеи ограничить цикл формулы sin(x)

Создание функции c/++ для вычисления sin(x)

Ссылка по формуле;
http://wiki.ubc.ca/images/math/4/4/  7/447a79826774707026bbefcd76962d3a.png

Но я не знаю, где остановить бесконечную сумму, и мне не нужны библиотеки для быстрого ответа. Я хочу сделать это. Я сделал факториал хорошо. (факт())

double sin(int x) {
    int tp=0;
    for(int k=1; ;k++)
    {
        tp+=pow(-1,k)*pow(x,2*k+1)/fact(2*k+1);
    }
    return tp;
}

person Community    schedule 01.10.2017    source источник
comment
int tp=0; : ты уверен?   -  person BLUEPIXY    schedule 01.10.2017
comment
Вы можете установить эпсилон-значение сходимости, останавливаясь, когда нет заметной разницы в сумме ряда. Кроме того, вам не нужно вычислять ни степени, ни факториалы, как показывает этот ответ.   -  person Weather Vane    schedule 01.10.2017
comment
@BLUEPIXY да, конечно, tp - это переменная суммы.   -  person    schedule 01.10.2017
comment
Если вам нужен целочисленный результат (-1.0, 0.0, 1.0) , его можно еще упростить.   -  person BLUEPIXY    schedule 01.10.2017
comment
Я реализовал эту функцию. Чтобы найти ответ sin() для заданного угла. Что я сделал, так это запустил цикл 8 раз. После этого значение fact() стало чрезвычайно большим, что сделало коэффициент pow(x,2*k+1)/fact(2*k+1) очень маленьким. Я рекомендую запустить цикл 8 раз, а не усложнять цикл некоторыми значениями эпсилон.   -  person Sreeram TP    schedule 01.10.2017
comment
@frogwine Я также предполагаю, что аргумент (int x) не в радианах, верно?   -  person BLUEPIXY    schedule 01.10.2017
comment
NB: если производительность является проблемой, обратите внимание, что ваш знаковый член, факториальные члены и члены x^n могут быть прогрессивно вычислены по мере выполнения итерации.   -  person Alnitak    schedule 01.10.2017
comment
@BLUEPIXY да, это не пример радиона; sin(90) возвращает значение 1.   -  person    schedule 01.10.2017
comment
@frogwine Формула выражения ряда основана на радианах. Поэтому, если вы хотите использовать эту формулу, вам нужно сначала преобразовать ее в радианы. Если вы хотите получить только целочисленный результат, вы можете просто упростить его с помощью остатка.   -  person BLUEPIXY    schedule 01.10.2017
comment
я сделал tp двойным. я сделаю твое сейчас.   -  person    schedule 01.10.2017
comment
Первоначально я думал также использовать счетчик эпсилон, но, попробовав его, теперь я также думаю, что проще просто использовать фиксированный счетчик итераций.   -  person Alnitak    schedule 01.10.2017
comment
BLUEPIXY и @Sreeram TP, вы мужчины! Спасибо вам двоим (и, конечно, другим помощникам). Я запустил свой цикл 8 раз и отправил параметр функции sin в радианах, это сработало! Расчет sin30 = 0,5, sin90 = 1,0 спасибо за все!   -  person    schedule 01.10.2017
comment
Приятно слышать, что это помогло. :)   -  person Sreeram TP    schedule 01.10.2017


Ответы (5)


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

Когда вы остановитесь, вы получите приблизительный результат греха.

Более того, int tp=0; мне не кажется правильным. Вы должны инициализировать его результатом формулы, когда k равно 0. И, что более важно, он должен быть типа double, поскольку int предназначен для целых чисел.

Кроме того, значение x должно быть не только в радианах, но и должно обычно находиться в диапазоне от -π до +π.

person gsamaras    schedule 01.10.2017
comment
Мало того, он должен быть типа double - person meowgoesthedog; 01.10.2017
comment
@gsamaras tp var - это сумма переменных. Я подвожу итоги. - person ; 01.10.2017
comment
tp должно быть двойным, поскольку значение, вычисленное по формуле, будет числом с плавающей запятой или двойным значением. Также возвращаемый тип функции — double. - person Sreeram TP; 01.10.2017
comment
NB: значение x должно быть не только в радианах, но и обычно должно быть в диапазоне от -π до +π - person Alnitak; 01.10.2017

Что вам нужно, так это значение эпсилон, значение, которое вы говорите, когда я буду более точным, чем то число, которое мы хотим остановить. Это будет выглядеть примерно так if(abs(prev_ans - cur_ans) < epsilon) break;

Выражаясь математическими словами, частичная сумма n и n+1 ряда различается настолько мало, что продолжение ряда не даст желаемой точности.

person Mitch    schedule 01.10.2017
comment
я сделал что-то ‹pre› double sin(int x) { double eps=8.85*pow(10,-12); двойной тп=0; двойной prev_ans; двойной кур_анс; for(int k=1;;k++) { cur_ans+=pow(-1,k)*pow(x,2*k+1)/fact(2*k+1); if(abs(prev_ans - cur_ans) ‹ eps) перерыв; prev_ans=cur_ans; } вернуть cur_ans; } ‹код› - person ; 01.10.2017
comment
@frogwine попробуйте изменить его так, чтобы грех принимал двойное значение, также убедитесь, что вы инициализируете prev_ans нулем. Также я сделал ошибку, вы должны использовать fabs(), как указано в другом ответе, поскольку вы сравниваете двойники - person Mitch; 01.10.2017
comment
abs — целочисленная функция, ей нужно fabs. - person Weather Vane; 01.10.2017

Опубликовано для справки, вот моя версия, в которой используется прогрессивный расчет различных компонентов расширения ряда вместо вызова внешней функции факториала O(n):

double mysin(double x)
{
    int sign = 1;
    int iteration = 1;
    double xn = x;
    double factorial = 1.0;
    double result = 0.0;
    double delta;

    while (iteration < 31) {
        delta = xn / factorial;
        result += sign * delta;
        xn *= x * x;
        sign = -sign;
        factorial *= ++iteration;
        factorial *= ++iteration;
    }
    return result;
}

Из моих тестов не видно значительного преимущества в точности (или производительности) при раннем прыжке, если fabs(delta) становится слишком маленьким, если только начальное значение x также не очень мало.

person Community    schedule 01.10.2017

Прежде чем мы пойдем дальше, вы должны использовать double для типов возврата и накопления, иначе результат будет (сильно) усечен.


Вы должны остановиться, когда добавление термина изменения не изменяет текущее накопленное значение суммирования, т. е. имеет место недополнение. Это происходит, когда:

Величина члена изменения меньше значения наименьшей двоичной цифры аккумулятора.

Чтобы проверить это, полезно извлечь показатель степени числа с плавающей запятой. Это сообщение SO предоставляет полезную функцию: frexp.

Затем мы можем проверить, что показатель степени члена изменения меньше, чем показатель степени аккумулятора минус количество битов в мантиссе типа double (52 бита). Если вам нужно узнать больше, выполните поиск по запросу Формат с плавающей запятой IEEE.


Еще один момент для улучшения числовой точности и скорости: давайте рассмотрим взаимосвязь между последовательными терминами:

введите здесь описание изображения

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


Образец кода:

// best to use an existing C-library define for this, if exists
#define DBL_MANTISSA_BIT 52

double mysin_new(double x)
{
   double term = x;
   double sum = term;

   for (int n = 1; ; n++) {
      // next term in the series
      term *= - (x / (2*n)) * (x / (2*n+1));

      // check if the exponent is small enough for us to stop
      int e_term, e_sum;
      frexp(term, &e_term);
      frexp(sum, &e_sum);
      if (e_term <= e_sum - DBL_MANTISSA_BIT) break;

      sum += term;
   }

   return sum;
}

Некоторые числовые тесты:

angle = 0.157080
  library  : 0.15643446504023086896f, 0011111111000100000001100000101101100111101010000101001101110101b
  mysin_old: 0.15643446504023086896f, 0011111111000100000001100000101101100111101010000101001101110101b
  mysin_new: 0.15643446504023089672f, 0011111111000100000001100000101101100111101010000101001101110110b

angle = 0.314159
  library  : 0.30901699437494739575f, 0011111111010011110001101110111100110111001011111110100101001111b
  mysin_old: 0.30901699437494745126f, 0011111111010011110001101110111100110111001011111110100101010000b
  mysin_new: 0.30901699437494739575f, 0011111111010011110001101110111100110111001011111110100101001111b

angle = 0.471239
  library  : 0.45399049973954674897f, 0011111111011101000011100010111000101011010001001101111000000000b
  mysin_old: 0.45399049973954674897f, 0011111111011101000011100010111000101011010001001101111000000000b
  mysin_new: 0.45399049973954674897f, 0011111111011101000011100010111000101011010001001101111000000000b

angle = 0.628319
  library  : 0.58778525229247313710f, 0011111111100010110011110010001100000100011101010101101001011110b
  mysin_old: 0.58778525229247313710f, 0011111111100010110011110010001100000100011101010101101001011110b
  mysin_new: 0.58778525229247313710f, 0011111111100010110011110010001100000100011101010101101001011110b

angle = 0.785398
  library  : 0.70710678118654746172f, 0011111111100110101000001001111001100110011111110011101111001100b
  mysin_old: 0.70710678118654746172f, 0011111111100110101000001001111001100110011111110011101111001100b
  mysin_new: 0.70710678118654746172f, 0011111111100110101000001001111001100110011111110011101111001100b

angle = 0.942478
  library  : 0.80901699437494745126f, 0011111111101001111000110111011110011011100101111111010010101000b
  mysin_old: 0.80901699437494734024f, 0011111111101001111000110111011110011011100101111111010010100111b
  mysin_new: 0.80901699437494734024f, 0011111111101001111000110111011110011011100101111111010010100111b

angle = 1.099557
  library  : 0.89100652418836778779f, 0011111111101100100000110010000000011101001111010010110001101100b
  mysin_old: 0.89100652418836767676f, 0011111111101100100000110010000000011101001111010010110001101011b
  mysin_new: 0.89100652418836767676f, 0011111111101100100000110010000000011101001111010010110001101011b

angle = 1.256637
  library  : 0.95105651629515353118f, 0011111111101110011011110000111000010011010001000101010011111111b
  mysin_old: 0.95105651629515353118f, 0011111111101110011011110000111000010011010001000101010011111111b
  mysin_new: 0.95105651629515353118f, 0011111111101110011011110000111000010011010001000101010011111111b

angle = 1.413717
  library  : 0.98768834059513777035f, 0011111111101111100110110010010010010100001011111110010001011100b
  mysin_old: 0.98768834059513777035f, 0011111111101111100110110010010010010100001011111110010001011100b
  mysin_new: 0.98768834059513777035f, 0011111111101111100110110010010010010100001011111110010001011100b

angle = 1.570796
  library  : 1.00000000000000000000f, 0011111111110000000000000000000000000000000000000000000000000000b
  mysin_old: 1.00000000000000022204f, 0011111111110000000000000000000000000000000000000000000000000001b
  mysin_new: 1.00000000000000000000f, 0011111111110000000000000000000000000000000000000000000000000000b

angle = 1.727876
  library  : 0.98768834059513777035f, 0011111111101111100110110010010010010100001011111110010001011100b
  mysin_old: 0.98768834059513777035f, 0011111111101111100110110010010010010100001011111110010001011100b
  mysin_new: 0.98768834059513777035f, 0011111111101111100110110010010010010100001011111110010001011100b

angle = 1.884956
  library  : 0.95105651629515364220f, 0011111111101110011011110000111000010011010001000101010100000000b
  mysin_old: 0.95105651629515364220f, 0011111111101110011011110000111000010011010001000101010100000000b
  mysin_new: 0.95105651629515375323f, 0011111111101110011011110000111000010011010001000101010100000001b

angle = 2.042035
  library  : 0.89100652418836789881f, 0011111111101100100000110010000000011101001111010010110001101101b
  mysin_old: 0.89100652418836800983f, 0011111111101100100000110010000000011101001111010010110001101110b
  mysin_new: 0.89100652418836800983f, 0011111111101100100000110010000000011101001111010010110001101110b

angle = 2.199115
  library  : 0.80901699437494745126f, 0011111111101001111000110111011110011011100101111111010010101000b
  mysin_old: 0.80901699437494756229f, 0011111111101001111000110111011110011011100101111111010010101001b
  mysin_new: 0.80901699437494756229f, 0011111111101001111000110111011110011011100101111111010010101001b

angle = 2.356194
  library  : 0.70710678118654757274f, 0011111111100110101000001001111001100110011111110011101111001101b
  mysin_old: 0.70710678118654768376f, 0011111111100110101000001001111001100110011111110011101111001110b
  mysin_new: 0.70710678118654757274f, 0011111111100110101000001001111001100110011111110011101111001101b

angle = 2.513274
  library  : 0.58778525229247324813f, 0011111111100010110011110010001100000100011101010101101001011111b
  mysin_old: 0.58778525229247324813f, 0011111111100010110011110010001100000100011101010101101001011111b
  mysin_new: 0.58778525229247324813f, 0011111111100010110011110010001100000100011101010101101001011111b

angle = 2.670354
  library  : 0.45399049973954685999f, 0011111111011101000011100010111000101011010001001101111000000010b
  mysin_old: 0.45399049973954685999f, 0011111111011101000011100010111000101011010001001101111000000010b
  mysin_new: 0.45399049973954691550f, 0011111111011101000011100010111000101011010001001101111000000011b

angle = 2.827433
  library  : 0.30901699437494750677f, 0011111111010011110001101110111100110111001011111110100101010001b
  mysin_old: 0.30901699437494745126f, 0011111111010011110001101110111100110111001011111110100101010000b
  mysin_new: 0.30901699437494745126f, 0011111111010011110001101110111100110111001011111110100101010000b

angle = 2.984513
  library  : 0.15643446504023097998f, 0011111111000100000001100000101101100111101010000101001101111001b
  mysin_old: 0.15643446504023095223f, 0011111111000100000001100000101101100111101010000101001101111000b
  mysin_new: 0.15643446504023095223f, 0011111111000100000001100000101101100111101010000101001101111000b

angle = 3.141593
  library  : 0.00000000000000012246f, 0011110010100001101001100000000000000000000000000000000000000000b
  mysin_old: 0.00000000000000023566f, 0011110010110000111110110010110111010110111000110001010101001000b
  mysin_new: 0.00000000000000023566f, 0011110010110000111110110010110111010110111000110001010101001001b

Новый адаптивный метод выигрывает в некоторых случаях по сравнению с исходным, рисует большинство из них и проигрывает в ряде других. (Обратите внимание, что предполагается, что библиотечный метод sin абсолютно точен до точности double)

person meowgoesthedog    schedule 01.10.2017

Я запустил цикл 8 раз, он приближается к истинному значению, если вы улучшите синхронизацию цикла. Спасибо @Sreeram TP

double sin(int x) {
    int tp=0;
    for(int k=1;k<8 ;k++)
    {
        tp+=pow(-1,k)*pow(x,2*k+1)/fact(2*k+1);
    }
    return tp;
}
person Community    schedule 24.07.2018