c++ 没有任何库的正弦函数

rfbsl7qr  于 2023-04-08  发布在  其他
关注(0)|答案(4)|浏览(143)

我刚开始学习c++。出于好奇,我创建了一个sine函数,没有使用任何库(除了iostream,用于输入和输出)。下面是我写的代码,它运行得很好。

#include<iostream>
using namespace std;
int factorial(int n);
double sin(double x, int n);
double pow(double x, int n);
int main(){
    double num;
    int n;
    cout << "Enter any Number" << endl;
    cin >> num;
    cout << "Enter n" <<  endl;
    cin >> n;
    cout << "sine of given x equals " << sin(num, n);
}
int factorial(int n){
    int a=1;
    for(int p=1; p<=n; p++){
        a=a*p;
    }
        return a;
}
double sin(double x, int n){
    double sine=0;
    for ( int a=1, b=1; a<n+n+1; a+=2, b++){
        sine=sine + (pow(-1, b+1))*(pow(x,a))/(factorial(a));
    }
    return sine;
}
double pow(double x, int n){
    double num=1;
    for (int a=1; a<=n; a++){
        num=num*x;
    }
    return num;
}

它用泰勒级数计算正弦,我也用n作为泰勒级数的项数,以提高精度,我对此有一定的疑问
1)我创建的sin函数,我通过反复试验发现,在for循环中,我必须写'a〈n+n+1',但如果我试图写'a〈2n+1',它会给我丑陋的编译错误。为什么会这样?我能做些什么来使它这样?
2)如果我尝试输入n的大值(〉15-16),它给出的答案是“nan”。为什么会这样?我认为double具有巨大的存储数字的容量(10^408)。如果我错了,请纠正我。或者可以做些什么来使它计算n的大值。
3)我知道我写的代码很难看,我不想使用任何库函数。无论如何,我可以做些什么来使这段代码在算法方面做得更好。有没有其他有效的方法,而不使用任何库。
4)还有其他的意见/提示/提示,以便将来学习更多?

flvlnr44

flvlnr441#

1.使用a < 2*n +1

  1. 15的阶乘是1,307,674,368,000。这个数字不能用int表示。你必须重新考虑你用来计算sine的泰勒级数项的代码。
    1.最好将你的工作代码发布在http://codereview.stackexchange.com上,以获得关于如何改进你的代码的反馈。
    泰勒级数展开式中的第N项为(-1)^(N-1)x^(2*N+1)/(2*N+1)!
    第(N+1)项为(-1)^(N)x^(2*(N+1)+1)/(2*(N+1)+1)!
    T(N+1) = -1*T(N)*x^2/((2*(N+1)+1)*(2*(N+1))
    当你使用这个逻辑时,你不需要powfactorial。你可以很容易地从第N项推导出第(N+t)项。起点T(0)就是x
    下面是一个示例程序:
#include <iostream>
#include <iomanip>

using namespace std;

double sin(double x, int n)
{
   double t = x;
   double sine = t;
   for ( int a=1; a<n; ++a)
   {
      double mult = -x*x/((2*a+1)*(2*a));
      t *= mult;
      sine += t;
   }
   return sine;
}

int main()
{
    double num;
    int n;
    cout << "Enter any Number" << endl;
    cin >> num;
    cout << "Enter n" <<  endl;
    cin >> n;
    cout << std::setprecision(20) << "sine of given x equals " << sin(num, n) << std::endl;

   return 0;
}

样品输入:

3.5
5

输出:

sine of given x equals -0.32838899588879211233

样品输入:

3.5
20

输出:

sine of given x equals -0.35078322768961955891

样品输入:

3.5
200

输出:

sine of given x equals -0.35078322768961955891

P.S.当N从20变为200时,输出没有变化。

vdgimpew

vdgimpew2#

1)你需要写

a < 2*n+1

你可以使用递归函数来使代码更好更清晰。

eaf3rand

eaf3rand3#

你将很难说服任何人相信你的说法“完美地工作”,特别是因为你继续描述问题。
1)在C中,2n+1不会将n加倍并将1添加到它上面。正确的表达式是(大概)2*n + 1。回去阅读C的任何基本介绍以理解为什么。
2)你计算sin()的方法将一个值提升到一个很大的幂。如果你将一个值提升到16次幂,它不需要一个特别大的值来溢出大多数浮点表示,即产生一个大于浮点类型所能表示的值。其结果在C中是未定义的,但是对于IEEE表示,结果是NaN
2a)您忽略的一个问题是factorial()增长非常快。(对应于所需的最小值)将在计算8!时溢出。32位有符号整数(在实践中常见,但不保证)在计算13!时会溢出。即使是64位有符号int(在某些编译器中确实存在)在计算21!时会溢出。(内置)有符号整型是未定义的行为。选择一个较大的整数不会解决这个问题,因为阶乘对于较大的值增长得更快-不是说从32位整数更改为64位整数只会提高计算1420之间的阶乘的能力。
3)最大的问题是你期望能够计算大的值(溢出变量),将它们相除,并得到数学上合理的结果。计算机不是这样工作的。并且从你的意识中去除任何需要使用pow()factorial()的概念-如果这两个溢出,将它们的结果相除是毫无意义的(而且,在你的情况下,两者都可能溢出)。你需要做的是看看泰勒级数中的连续项,并利用它们之间的数学关系。
泰勒级数的第一项(当计算sin(x)时)是x。第二个是-x^3/3! [使用^表示“的幂”,虽然这不是有效的C
]。第三个是+x^5/5!。第四个是-x^7/7!。考虑第一个和第二个项之间的关系。然后考虑第二项和第三项之间的关系,将其推广,您将有一种使用泰勒斯级数计算sin()的方法,大大降低了溢出的风险。
而且,如果有人试图告诉你使用“巨大的整数”(一种没有上限的整数类型)和“巨大的浮点类型”,忽略它们。是的,这些东西有它们的位置,但你最好避免使用它们。在这种情况下,避免使用它们是可能的。

yzuktlbb

yzuktlbb4#

以下是我使用泰勒斯展开的wikipedia示例,我认为这些是最快的。您可以删除包含“t+=...”的最后一行,以获得低精度和高性能:

#define _USE_MATH_DEFINES
#include <math.h>
inline float Sin(float x) 
{
    // if x is in between -pi and pi no need to fmod
    //x = fmodf(x + M_PI, 2.0f * M_PI) - M_PI; 
    float xx = x * x * x; // x^3
    float t = x - (xx  / 6.0f); // 6 = !3
    t += (xx *= x * x) / 120.0f; // 120 = !5
    t -= (xx *= x * x) / 5040.0f; // 5040 = !7 
    t += (xx * x * x)  / 362880.0f; // 362880 = !9 
    return t;
}

inline float Cos(float x) 
{
    //x = fmodf(x + M_PI, 2.0f * M_PI) - M_PI;
    float xx = x * x; // x^2
    float t = 1.0f - (xx / 2.0f); // 2 = !2
    t += (xx *= x * x) / 24.0f;  // 24.0f = !4
    t -= (xx *= x * x) / 720.0f; // 720.0f = !6
    t += (xx * x * x) / 40320.0f; // 40320 = !8
    return t;
}

相关问题