为什么C#中的sin(x)函数返回NaN而不是数字
我用C#写了这个函数来计算sin(x)。但是当我尝试使用x=3.14时,sin x的打印结果是NaN(不是数字), 但在调试时,它非常接近于0.001592653 这个值不是太大,也不是太小。那南怎么会出现在这里呢为什么C#中的sin(x)函数返回NaN而不是数字,c#,loops,double,nan,trigonometry,C#,Loops,Double,Nan,Trigonometry,我用C#写了这个函数来计算sin(x)。但是当我尝试使用x=3.14时,sin x的打印结果是NaN(不是数字), 但在调试时,它非常接近于0.001592653 这个值不是太大,也不是太小。那南怎么会出现在这里呢 static double pow(double x, int mu) { if (mu == 0) return 1; if (mu == 1) retu
static double pow(double x, int mu)
{
if (mu == 0)
return 1;
if (mu == 1)
return x;
return x * pow(x, mu - 1);
}
static double fact(int n)
{
if (n == 1 || n == 0)
return 1;
return n * fact(n - 1);
}
static double sin(double x)
{
var s = x;
for (int i = 1; i < 1000; i++)
{
s += pow(-1, i) * pow(x, 2 * i + 1) / fact(2 * i + 1);
}
return s;
}
public static void Main(String[] param)
{
try
{
while (true)
{
Console.WriteLine("Enter x value: ");
double x = double.Parse(Console.ReadLine());
var sinX = sin(x);
Console.WriteLine("Sin of {0} is {1}: " , x , sinX);
Console.ReadLine();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
静态双功率(双x,整数μ)
{
if(mu==0)
返回1;
if(mu==1)
返回x;
返回x*pow(x,mu-1);
}
静态双事实(int n)
{
如果(n==1 | | n==0)
返回1;
返回n*事实(n-1);
}
静态双正弦(双x)
{
var s=x;
对于(int i=1;i<1000;i++)
{
s+=pow(-1,i)*pow(x,2*i+1)/事实(2*i+1);
}
返回s;
}
公共静态void Main(字符串[]参数)
{
尝试
{
while(true)
{
Console.WriteLine(“输入x值:”);
double x=double.Parse(Console.ReadLine());
var sinX=sin(x);
WriteLine({0}的Sin是{1}:,x,sinX);
Console.ReadLine();
}
}
捕获(例外情况除外)
{
控制台写入线(例如消息);
}
}
它失败是因为pow(x,2*i+1)
和fact(2*i+1)
最终都返回无穷大
在我的例子中,是当x=4
,i=256
时
请注意,pow(x,2*i+1)
=4^(2*257)
=-一个非常大的数字,刚好超过一个双精度的最大值,约为1.79769313486232 x 10^308
您可能只对使用Math.Sin(x)
还要注意,fact(2*i+1)=513=
大于
10^1000倍。当x==3.14,i==314时,则得到无穷大:
?pow(-1, 314)
1.0
?pow(x, 2 * 314 + 1)
Infinity
? fact(2 * 314 + 1)
Infinity
这里的问题是理解“实数”的浮点表示 双精度数字虽然允许较大范围的值,但精度仅为15到17位小数 在本例中,我们计算的值介于-1和1之间 我们用正弦函数的级数展开式来计算它的值,它基本上是项和。在这种扩展中,随着我们的前进,术语变得越来越小 当条款达到小于1e-17的值时,将其添加到现有条款中不会产生任何差异。这是因为我们只有52位的精度,当我们得到一个小于1e-17的项时,这些精度就用完了 因此,您不应该执行恒定的1000个循环,而应该执行以下操作:
static double sin(double x)
{
var s = x;
for (int i = 1; i < 1000; i++)
{
var term = pow(x, 2 * i + 1) / fact(2 * i + 1);
if (term < 1e-17)
break;
s += pow(-1, i) * term;
}
return s;
}
静态双正弦(双x)
{
var s=x;
对于(int i=1;i<1000;i++)
{
var项=功率(x,2*i+1)/事实(2*i+1);
如果(术语<1e-17)
打破
s+=功率(-1,i)*项;
}
返回s;
}
X的值是多少?我使用了1的值,结果是0.8414709848078965-解释重现问题的步骤..我使用x=3.14,这会使错误发生OK,可能是我必须更改算法:)我想为基准编写这个函数,不是为了计算的目的:)使用ILSpy,看看Math.Sin的内部实现。我认为verdana没有使用Math.Sin
,因为练习的目的是将Sin函数作为一个系列来计算。@JeremyCompson所有的trig函数都是在CLR中以利用该平台的本机代码实现的能力,所以ILSpy不会告诉你任何有用的东西。