为什么这个 sin(x) 函数在 C# return NaN 而不是数字
Why this sin(x) function in C# return NaN instead of a number
我用 C# 编写了这个函数来计算 sin(x)。但是当我尝试使用 x = 3.14 时,sin X 的打印结果是 NaN(不是数字),
但在调试时,它非常接近 0.001592653
价值不会太大,也不会太小。那么NaN怎么会出现在这里呢?
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);
}
}
它失败了,因为 pow(x, 2 * i + 1)
和 fact(2 * i + 1)
最终都 return Infinity
。
我的情况是 x = 4
, i = 256
.
请注意 pow(x, 2 * i + 1)
= 4 ^ (2 * 257)
= 2.8763090157797054523668883052624395737887631663 × 10^309 - 一个非常大的数字,刚好超过 double 的最大值,大约为 1.79769313486232 x 10 ^ 308。
您可能只对使用 Math.Sin(x)
感兴趣
还要注意 fact(2 * i + 1) = 513! =
an even more ridiculously large number
which is more than 10^1000
times larger than the estimated number of atoms in the observable universe.
当 x == 3.14 和 i == 314 时,你得到无穷大:
?pow(-1, 314)
1.0
?pow(x, 2 * 314 + 1)
Infinity
? fact(2 * 314 + 1)
Infinity
这里的问题是理解 'real' 数字的浮点表示。
双数虽然允许大范围的值,但精度仅为 15 到 17 位小数。
在这个例子中,我们正在计算一个介于 -1 和 1 之间的值。
我们通过使用它的级数展开来计算 sin 函数的值,它基本上是项的总和。在那个扩展中,随着我们的进行,项变得越来越小。
当项达到小于 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;
}
我用 C# 编写了这个函数来计算 sin(x)。但是当我尝试使用 x = 3.14 时,sin X 的打印结果是 NaN(不是数字), 但在调试时,它非常接近 0.001592653 价值不会太大,也不会太小。那么NaN怎么会出现在这里呢?
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);
}
}
它失败了,因为 pow(x, 2 * i + 1)
和 fact(2 * i + 1)
最终都 return Infinity
。
我的情况是 x = 4
, i = 256
.
请注意 pow(x, 2 * i + 1)
= 4 ^ (2 * 257)
= 2.8763090157797054523668883052624395737887631663 × 10^309 - 一个非常大的数字,刚好超过 double 的最大值,大约为 1.79769313486232 x 10 ^ 308。
您可能只对使用 Math.Sin(x)
还要注意 fact(2 * i + 1) = 513! =
an even more ridiculously large number
which is more than 10^1000
times larger than the estimated number of atoms in the observable universe.
当 x == 3.14 和 i == 314 时,你得到无穷大:
?pow(-1, 314)
1.0
?pow(x, 2 * 314 + 1)
Infinity
? fact(2 * 314 + 1)
Infinity
这里的问题是理解 'real' 数字的浮点表示。
双数虽然允许大范围的值,但精度仅为 15 到 17 位小数。
在这个例子中,我们正在计算一个介于 -1 和 1 之间的值。
我们通过使用它的级数展开来计算 sin 函数的值,它基本上是项的总和。在那个扩展中,随着我们的进行,项变得越来越小。
当项达到小于 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;
}