在没有 Math.sin 函数的情况下在 Java 中实现正弦

Implement Sine in Java without Math.sin function

我正在尝试在 Java 中实现正弦函数而不使用 Math.sin(x)。所以我试图用泰勒级数来实现这一点。不幸的是,这段代码给出了错误的结果。

不知道什么是泰勒级数的可以看看:

这是我创建的代码片段:

public static double sin(double a) {
   double temp = 1;
   int denominator = -1;
   if(a == Double.NEGATIVE_INFINITY || !(a < Double.POSITIVE_INFINITY)) {
      return Double.NaN;
   } 
   if(a != 0) {
      for (int i = 0; i <= a; i++) {
         denominator += 2;
         if(i % 2 == 0) {
            temp = temp + (Math.pow(a, denominator) / Factorial.factorial(denominator));
         } else {
            temp = temp - (Math.pow(a, denominator) / Factorial.factorial(denominator));
         }
      }
   }
   return temp;
}

我找不到我犯的错误。你呢?

您的问题是,您正在使用要为正弦函数计算的值作为分母的极限。泰勒级数被评估为函数的极限接近无穷大。在这种情况下,您只是根据输入值的大小对其进行评估,这实际上没有意义。您应该将 for 循环比较替换为 i < x,其中 x 是一个常数,表示您希望它达到的精确度(对于低至 20 左右的值,该函数将相当准确)。

您的代码中存在两个主要问题。第一个问题是您将 i0 循环到 a。这意味着,如果 a 是一个负值,for 循环甚至不会开始,您的结果将始终是 1.0。而如果 a 为正,则循环开始,但在 (int) a 次迭代后停止,这没有多大意义,因为当迭代 n 趋于无穷大时,泰勒近似可以正常工作。

第二个主要问题是您没有对输入值进行足够的控制a。 正如我在

中所说的

The real Taylor expansion centered in x0 is:

where Rn is the Lagrange Remainder

Note that Rn grows fast as soon as x moves away from the center x0.

Since you are implementing the Maclaurin series (Taylor series centered in 0) and not the general Taylor series, your function will give really wrong results when trying to calculate sin(x) for big values of x.

因此,在 for 循环之前,您必须将域至少减小到 [-pi, pi]...如果将其减小到 [=31 会更好=][0, pi] 并利用正弦奇偶校验。

工作代码:

public static double sin(double a) {

    if (a == Double.NEGATIVE_INFINITY || !(a < Double.POSITIVE_INFINITY)) {
        return Double.NaN;
    }

    // If you can't use Math.PI neither,
    // you'll have to create your own PI
    final double PI = 3.14159265358979323846;

    // Fix the domain for a...

    // Sine is a periodic function with period = 2*PI
    a %= 2 * PI;
    // Any negative angle can be brought back
    // to it's equivalent positive angle
    if (a < 0) {
        a = 2 * PI - a;
    }
    // Also sine is an odd function...
    // let's take advantage of it.
    int sign = 1;
    if (a > PI) {
        a -= PI;
        sign = -1;
    }
    // Now a is in range [0, pi].


    // Calculate sin(a)

    // Set precision to fit your needs.
    // Note that 171! > Double.MAX_VALUE, so
    // don't set PRECISION to anything greater
    // than 84 unless you are sure your
    // Factorial.factorial() can handle it
    final int PRECISION = 50;
    double temp = 0;
    for (int i = 0; i <= PRECISION; i++) {
        temp += Math.pow(-1, i) * (Math.pow(a, 2 * i + 1) / Factorial.factorial(2 * i + 1));
    }

    return sign * temp;

}