簡體   English   中英

為什么C#中的sin(x)函數返回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)最終都返回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! = fact(2 * i + 1) = 513! = an even more ridiculously large number ,比可觀察的宇宙中估計的原子數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;
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM