【问题标题】:Creating a custom sine function创建自定义正弦函数
【发布时间】:2018-07-23 08:21:09
【问题描述】:

我尝试使用 cTaylor Series 创建自定义 sine 函数,以计算系列中的 10 个术语的 sin,但是当我尝试查找 @987654327 时得到错误的结果@其中x > 6

它适用于 -5 < x < 5,但超出该范围的任何内容都不会产生正确的结果。

我希望sin(10) 返回接近-0.5440 的值,但得到1418.0269775391

我已将所有内容放在一个文件中,这样更容易。

#include <stdio.h>
#include <stdlib.h>

double factorial(double n);
double power(double n, double pow);
double sine(double n);

// This is supposed to all go in a .c file and reference the .h stuff above
// This is the actual implementation of the functions declared above
double factorial(double n) {
    // 0! = 1 so just return it
    if(n == 0) {
        return 1;
    }
    // Recursively call factorial with n-1 until n == 0
    return n * (factorial(n - 1)); 
}


double power(double n, double power) {
    double result = n;
    // Loop as many times as the power and just multiply itself power amount of times
    for(int i = 1; i < power; i++) {
        result = n * result;
    }
    return result;
}

double sine(double n) {
    double result = n;
    double coefficent = 3; // Increment this by 2 each loop
    for(int i = 0; i < 10; i++) { // Change 10 to go out to more/less terms
        double pow = power(n, coefficent);
        double frac = factorial(coefficent);
        printf("Loop %d:\n%2.3f ^ %2.3f = %2.3f\n", i, n, coefficent, pow);
        printf("%2.3f! = %2.3f\n", coefficent, frac);

        // Switch between adding/subtracting
        if(i % 2 == 0) { // If the index of the loop is divided by 2, the index is even, so subtract
            result = result - (pow/frac); // x - ((x^3)/(3!)) - ((x^5)/(5!))...
        } else {
            result = result + (pow/frac); // x - ((x^3)/(3!)) + ((x^5)/(5!))...
        }
        coefficent = coefficent + 2;
        printf("Result = %2.3f\n\n", result);
    }
    return result;
}

// main starting point. This is suppossed to #include "functions.c" which contain the above functions in it
int main(int argc, char** argv) {

    double number = atof(argv[1]); // argv[1] = "6"
    double sineResult = sine(number);
    printf("%1.10f", sineResult);
    return (0);
}

【问题讨论】:

  • 如果你之前没有尝试过,现在是learn how to debug our programs的好时机。
  • 为什么你要double number = atof("5")?为什么不简单地double number = 5.0?还是直接拨打sine(5.0)?或者由于该函数采用int 参数,int number = 5;sine(5)
  • 您在很多地方都在使用int。最终其中一个会溢出。例如power(10, 10) 不会返回正确答案(因为正确答案是 100 亿,不适合 32 位 int)。
  • sine(n) 中,首先使用n = fmod(n, 2*pi); 或等效项..
  • 弧度的质量范围缩小是一个比计算sine() 更困难的问题。见https://www.csee.umbc.edu/~phatak/645/supl/Ng-ArgReduction.pdf

标签: c factorial trigonometry taylor-series


【解决方案1】:

正如我在Python: Calculate sine/cosine with a precision of up to 1 million digits中已经说过的那样

x0为中心的真正泰勒展开式是:

其中 Rn拉格朗日余数

请注意,Rnx 远离中心时会快速增长 x0.

由于您正在实施 麦克劳林级数(泰勒级数 以 0 为中心)而不是一般的 泰勒级数,你的函数 尝试计算 sin(x) 时会给出非常错误的结果 x 的大值。

因此,在 sine() 函数中的 for 循环之前,您必须将域减少到至少 [-pi, pi]...最好将其减少到 [ 0, pi] 并利用正弦奇偶校验。

要修复您的代码,您需要来自math.hfmod(),所以您可以这样做:

#include <math.h>

// Your code

double sine (double n) {
    // Define PI
    const double my_pi = 3.14159265358979323846;
    // Sine's period is 2*PI
    n = fmod(n, 2 * my_pi);
    // Any negative angle can be brought back
    // to it's equivalent positive angle
    if (n < 0) {
        n = 2 * my_pi - n;
    }
    // Sine is an odd function...
    // let's take advantage of it.
    char sign = 1;
    if (n > my_pi) {
        n -= my_pi;
        sign = -1;
    }
    // Now n is in range [0, PI].

    // The rest of your function is fine

    return sign * result;
}

现在如果你真的讨厌math.h 模块,你可以像这样实现自己的fmod()

double fmod(double a, double b)
{
    double frac = a / b;
    int floor = frac > 0 ? (int)frac : (int)(frac - 0.9999999999999999);
    return (a - b * floor);
}

Try it online!

【讨论】:

    【解决方案2】:

    在进行更正后,如我对问题的 cmets 中所列,建议的代码如下所示:

    #include <stdio.h>
    #include <stdlib.h>
    
    double factorial(double n);
    double power(double n, double pow);
    double sine(double n);
    
    // This is supposed to all go in a .c file and reference the .h stuff above
    // This is the actual implementation of the functions declared above
    double factorial(double n) {
        // 0! = 1 so just return it
        if(n == 0) {
            return 1;
        }
        // Recursively call factorial with n-1 until n == 0
        return n * (factorial(n - 1));
    }
    
    
    double power(double n, double power) {
        double result = n;
        // Loop as many times as the power and just multiply itself power amount of times
        for(int i = 1; i < power; i++) {
            result = n * result;
        }
        return result;
    }
    
    double sine(double n) {
        double result = n;
        double coefficent = 3.0; // Increment this by 2 each loop
    
        for(int i = 0; i < 10; i++) { // Change 10 to go out to more/less terms
            double pow = power(n, coefficent);
            double frac = factorial(coefficent);
            printf("Loop %d:\n%2.3f ^ %2.3f = %2.3f\n", i, n, coefficent, pow);
            printf("%2.3f! = %2.3f\n", coefficent, frac);
    
            // Switch between adding/subtracting
            if(i % 2 == 0) { // If the index of the loop is divided by 2, the index is even, so subtract
                result = result - (pow/frac); // x - ((x^3)/(3!)) - ((x^5)/(5!))...
            } else {
                result = result + (pow/frac); // x - ((x^3)/(3!)) + ((x^5)/(5!))...
            }
            coefficent = coefficent + 2;
            printf("Result = %2.3f\n\n", result);
        }
        return result;
    }
    
    
    // main starting point. This is suppossed to #include "functions.c" which contain the above functions in it
    int main( void )
    {
        double number = atof("6");
        double sineResult = sine(number);
        printf("%1.10f", sineResult);
        return (0);
    }
    

    结果输出如下:

    Loop 0:
    6.000 ^ 3.000 = 216.000
    3.000! = 6.000
    Result = -30.000
    
    Loop 1:
    6.000 ^ 5.000 = 7776.000
    5.000! = 120.000
    Result = 34.800
    
    Loop 2:
    6.000 ^ 7.000 = 279936.000
    7.000! = 5040.000
    Result = -20.743
    
    Loop 3:
    6.000 ^ 9.000 = 10077696.000
    9.000! = 362880.000
    Result = 7.029
    
    Loop 4:
    6.000 ^ 11.000 = 362797056.000
    11.000! = 39916800.000
    Result = -2.060
    
    Loop 5:
    6.000 ^ 13.000 = 13060694016.000
    13.000! = 6227020800.000
    Result = 0.037
    
    Loop 6:
    6.000 ^ 15.000 = 470184984576.000
    15.000! = 1307674368000.000
    Result = -0.322
    
    Loop 7:
    6.000 ^ 17.000 = 16926659444736.000
    17.000! = 355687428096000.000
    Result = -0.275
    
    Loop 8:
    6.000 ^ 19.000 = 609359740010496.000
    19.000! = 121645100408832000.000
    Result = -0.280
    
    Loop 9:
    6.000 ^ 21.000 = 21936950640377856.000
    21.000! = 51090942171709440000.000
    Result = -0.279
    
    -0.2793866930
    

    【讨论】:

      【解决方案3】:

      泰勒展开式有一个错误,这取决于参数范围以及泰勒展开式的顺序。我相信你已经超出了争论的界限。有关更多示例,请参见此处:www.dotancohen.com/eng/taylor-sine.php

      【讨论】:

        猜你喜欢
        • 2011-11-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多