2016-01-15 4 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, это делает ошибку happend – 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, что более чем 10^1000 раз больше, чем estimated number of atoms in the observable universe.

+0

Хорошо, может быть, мне нужно изменить алгоритм :) – Andiana

+1

Я хочу написать эту функцию для эталона, а не для цели расчета :) – Andiana

+0

Используйте ILSpy и посмотрите на внутреннюю реализацию Math.Sin. –

2

Когда х == 3,14 и я == 314, то вы получаете бесконечность:

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

Проблемы здесь является пониманием плавающего представления точечного «реальных» чисел.

Двойные номера, допускающие большой диапазон значений, имеют точность от 15 до 17 десятичных цифр.

В этом примере мы вычисление значения между -1 и 1.

Вычислит значение функции грех, используя разложение в ряде его, который является в основном суммой членов. При этом разложении члены становятся все меньше и меньше по мере продвижения.

Когда термины достигли значения менее 1е-17, добавив их к тому, что уже там, не будет иметь никакого значения. Это происходит потому, что у нас есть только 52 бит точности, которые используются к тому времени, когда мы доходим до срока менее 1е-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; 
    }