【问题标题】:Why this sin(x) function in C# return NaN instead of a number为什么 C# 中的 sin(x) 函数返回 NaN 而不是数字
【发布时间】:2016-04-20 13:29:03
【问题描述】:

我用 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);
            }
        }

【问题讨论】:

  • X 是什么值?我使用了 1 的值,结果为 0.8414709848078965 - 解释重现问题的步骤..
  • 我使用 x = 3.14,它使错误发生

标签: c# loops double nan trigonometry


【解决方案1】:

它失败了,因为pow(x, 2 * i + 1)fact(2 * i + 1) 最终都返回Infinity

就我而言,是x = 4i = 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 number10^1000estimated number of atoms in the observable universe 倍。

【讨论】:

  • 好的,可能我必须改变算法:)
  • 我想写这个函数作为一个基准,而不是为了计算目的:)
  • 使用 ILSpy 并查看 Math.Sin 内部实现。
  • 我认为 verdana 没有使用Math.Sin,因为练习的目的是将 sin 函数计算为一个系列。
  • @JeremyThompson 所有的三角函数都在 CLR 中以利用平台功能的本机代码实现,因此 ILSpy 不会告诉您任何有用的信息。
【解决方案2】:

当 x == 3.14 和 i == 314 时,您会得到 Infinity:

?pow(-1, 314)
1.0
?pow(x, 2 * 314 + 1)
Infinity
? fact(2 * 314 + 1)
Infinity

【讨论】:

    【解决方案3】:

    这里的问题是对“实”数的浮点表示的理解。

    双数同时允许大范围的值,其精度只有 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;
        }
    

    【讨论】:

      猜你喜欢
      • 2022-11-21
      • 2020-09-02
      • 2017-06-09
      • 2013-08-19
      • 2020-11-13
      • 1970-01-01
      • 2011-02-17
      相关资源
      最近更新 更多