为什么显示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^50
和 50!
会溢出,然后除以它们会得到 infinity/infinity=NAN
。
在您的原始版本中 fact
值不适合整数(您的值溢出到 0
),然后 0
上的除法给您无穷大,在减去另一个无穷大后给您 NAN
.
如果您在编译系统时启用了任何合理级别的警告,您会立即发现您没有使用变量 accuracy
。这和你的 fact
函数 returns a long int
只是你问题的一小部分。即使您更正了这些问题,您也永远不会 sin(45)
使用您的算法获得好的结果。
问题在于 x=45
,sin(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 是对称的。你甚至可以做得更好。三角函数的实现充分利用了这些行为,但它们通常不使用泰勒近似。
好的,所以我正在做一个程序,我试图让右侧的结果等同于左侧的结果,精度为 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^50
和 50!
会溢出,然后除以它们会得到 infinity/infinity=NAN
。
在您的原始版本中 fact
值不适合整数(您的值溢出到 0
),然后 0
上的除法给您无穷大,在减去另一个无穷大后给您 NAN
.
如果您在编译系统时启用了任何合理级别的警告,您会立即发现您没有使用变量 accuracy
。这和你的 fact
函数 returns a long int
只是你问题的一小部分。即使您更正了这些问题,您也永远不会 sin(45)
使用您的算法获得好的结果。
问题在于 x=45
,sin(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 是对称的。你甚至可以做得更好。三角函数的实现充分利用了这些行为,但它们通常不使用泰勒近似。