为什么显示nan?

Why does it show nan?

好的,所以我正在做一个程序,我试图让右侧的结果等同于左侧的结果,精度为 0.0001%
sin x = x - (x^3)/3! + (x^5)/5! + (x^7)/7! +....

#include<iostream>
#include<iomanip>
#include<math.h>

using namespace std;

long int fact(long int n)
{
    if(n == 1 || n == 0)
        return 1;
    else
        return n*fact(n-1);
}

int main()
{
    int n = 1, counts=0; //for sin
    cout << "Enter value for sin" << endl;
    long double x,value,next = 0,accuracy = 0.0001;
    cin >> x;
    value = sin(x);
    do
    {
        if(counts%2 == 0)
            next = next + (pow(x,n)/fact(n));
        else
            next = next - (pow(x,n)/fact(n));
        counts++;
        n = n+2;
    } while((fabs(next - value))> 0);
    cout << "The value of sin " << x << " is " << next << endl;
}


假设我为 x 输入 45 我得到结果
nan 中 sin 45 的值。


谁能帮我指出我哪里做错了?

如果选择 45 意味着您认为输入是度数,您应该重新考虑并且可能应该减少 mod 2 Pi。

先修复两个bug:

    long double fact(long int n)
...
      }while((fabs(next - value))> accuracy);

fact 的return 值如果是long int 会很快溢出。即使 long double,fact 的 return 值最终也会溢出。当您与 0 而不是 accuracy 进行比较时,答案永远不够正确,因此只有 nan 可以阻止 while

由于舍入误差,您仍然永远不会收敛(虽然 pow 给出的值大于 fact 您正在计算大数之间的差异,这会累积显着的舍入误差,然后永远不会被删除).因此,您可以在循环的每个步骤中增加 n 之前停止计算 long double m=pow(x,n)/fact(n); 并使用:

}while(m > accuracy*.5);

此时,要么答案具有指定的准确度,要么剩余误差主要由舍入误差决定,进一步迭代将无济于事。

关于 pow 我引用 here 的话:

Return value

If no errors occur, base raised to the power of exp (or iexp) (baseexp), is returned.

If a domain error occurs, an implementation-defined value is returned (NaN where supported)

If a pole error or a range error due to overflow occurs, ±HUGE_VAL, ±HUGE_VALF, or ±HUGE_VALL is returned.

If a range error occurs due to underflow, the correct result (after rounding) is returned.

进一步阅读:

Error handling

...

except where specified above, if any argument is NaN, NaN is returned

所以基本上,因为 n 正在增加并且你有很多循环 pow returns NaN(你使用的编译器显然支持它)。剩下的就是算术。你用溢出的值计算。

我相信您正在尝试使用其泰勒级数来近似 sin(x)。我不确定这是否可行。

也许您可以尝试在点击 NaN 后立即停止循环,而不是更新变量 next 并简单地输出它。这是我相信你的算法最接近的结果。

首先你的 while 条件应该是 while((fabs(next - value))> accuracy)fact 应该 return long double。 当您更改它时,它仍然不适用于 45 的值。原因是这个泰勒级数对于大值收敛太慢。 这里是公式中的误差项

这里k是迭代次数a=0,函数是sin。为了使条件变为假45^(k+1)/(k+1)!乘以sin的某个绝对值或 cos(取决于第 k 个导数)(介于 0 和 1 之间)应小于 0.0001。 好吧,在这个公式中,50 的值仍然很大(我们应该预计误差在 1.3*10^18 左右,这意味着我们肯定会进行超过 50 次迭代)。 45^5050! 会溢出,然后除以它们会得到 infinity/infinity=NAN。 在您的原始版本中 fact 值不适合整数(您的值溢出到 0),然后 0 上的除法给您无穷大,在减去另一个无穷大后给您 NAN.

如果您在编译系统时启用了任何合理级别的警告,您会立即发现您没有使用变量 accuracy。这和你的 fact 函数 returns a long int 只是你问题的一小部分。即使您更正了这些问题,您也永远不会 sin(45) 使用您的算法获得好的结果。

问题在于 x=45sin(x) 的泰勒展开中的项直到 n=45 才会开始减少。这是一个大问题,因为 4545/45!是一个非常大的数字,2428380447472097974305091567498407675884664058685302734375 / 1171023117375434566685446533210657783808,或大约 2*10[=30==]18[=32]。您的算法最初添加和减去巨大的数字,这些数字在 20+ additions/subtractions 后才开始减少,最终希望结果介于 -1 和 +1 之间。给定输入值 45 并使用本机浮点类型,这是一个无法实现的希望。

您可以在您的算法中使用一些 BigNum 类型(互联网上充斥着它们),但是当您只需要四位精度时,这就太过分了。或者,您可以利用 sin(x)sin(x+2*pi)=sin(x) 的周期性。输入值 45 相当于 1.017702849742894661522992634...(模 2*pi)。您的算法对于 1.017702849742894661522992634.

的输入非常有效

您可以做得更好,但是对输入值取模 2*pi 是计算正弦和余弦的合理算法的第一步。更好的是,您可以使用 sin(x+pi)=-sin(x) 的事实。这使您可以将范围从 -infinity 到 +infinity 缩小到 0 到 pi。更好的是,您可以利用以下事实:在 0 和 pi 之间,sin(x) 关于 pi/2 是对称的。你甚至可以做得更好。三角函数的实现充分利用了这些行为,但它们通常不使用泰勒近似。