2016-01-15 109 views
2

我有這個函數寫在C#中來計算sin(x)。但是當我用x = 3.14進行嘗試時,sin X的打印結果是NaN(不是數字),但是在調試時,它非常接近0.001592653 值不是太大,也不是太小。那麼NaN怎麼會出現在這裏呢?爲什麼這個sin(x)函數在C#中返回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); 
      } 
     } 
+0

什麼樣的價值是X?我使用的值爲1,它出來爲0.8414709848078965 - 解釋重現問題的步驟.. –

+0

我使用x = 3.14,它是使錯誤發生 – Andiana

回答

3

它失敗,因爲這兩個pow(x, 2 * i + 1)fact(2 * i + 1)最終返回Infinity

在我的情況下,這是x = 4,i = 256

注意pow(x, 2 * i + 1) = 4^(2 * 257) = 2.8763090157797054523668883052624395737887631663 × 10^309 - 一個愣神大量這僅僅是在雙重的最大值,這大約是1.79769313486232×10^308

您可能會感興趣的只是用Math.Sin(x)

另請注意,fact(2 * i + 1) = 513! =an even more ridiculously large number這比estimated number of atoms in the observable universe10^1000倍。

+0

好吧,可能是我必須改變算法:) – Andiana

+1

我想爲基準編寫此函數,而不是爲了計算:) – Andiana

+0

使用ILSpy並查看Math.Sin的內部實現。 –

2

當x == 3.14和我== 314,那麼你得到無限:

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

這裏的問題是浮動的「真正的」數點表示的理解。

允許大範圍值的雙數字只有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; 
    } 
相關問題