【问题标题】:Taylor Series to calculate cosine (getting output -0.000 for cosine(90))泰勒级数计算余弦(余弦(90)的输出为-0.000)
【发布时间】:2020-02-04 19:21:46
【问题描述】:

我为泰勒级数编写了以下函数来计算余弦。

double cosine(int x) {
    x %= 360; // make it less than 360
    double rad = x * (PI / 180);
    double cos = 0;

    int n;

    for(n = 0; n < TERMS; n++) { 
        cos += pow(-1, n) * pow(rad, 2 * n) / fact(2 * n);
    }
    return cos;
}

我的问题是,当我输入 90 时,我得到的答案是 -0.000000。 (为什么我得到 -0.000 而不是 0.000?)

谁能解释我为什么以及如何解决这个问题?
我认为这是由于双精度。 这是 main() :

int main(void){
int y;
//scanf("%d",&y);
y=90;
printf("sine(%d)= %lf\n",y, sine(y));
printf("cosine(%d)= %lf\n",y, cosine(y));

return 0;
}

【问题讨论】:

  • 一个可能的罪魁祸首是您没有显示的打印程序。并且不要在包含 math.h 时创建名为 cos 的变量。
  • 我不确定您要询问的问题是什么。 cos(90) 的值是 0。-0.000000 本质上是 0。顺便说一句,有一种更有效的方法来计算连续项。每个术语只是-(rad * rad)/(2*n*(2*n-1)) 乘以前一个术语。因此,为此目的,在每次循环迭代中保存术语值。
  • 使用%g而不是%f,你会看到你的结果倾向于哪个值
  • 由于cos(90 + a little bit)0 - a little bit,这种事情很可能发生。您的代码(可能)工作正常。 “永远不要比较浮点数是否相等。” -0 对于所有实际目的都足够接近 0。
  • Lurker,为什么我得到的是 -0.000 而不是 0.000?

标签: c precision trigonometry taylor-series


【解决方案1】:

完全可以预料,无论您的计算方法有多好,对于 anything 的余弦,您都无法获得精确的零输出。这是浮点工作原理的基础。

余弦的数学零点是 pi/2 的奇数倍。因为 pi 是无理数,所以它不能完全表示为 double(或任何浮点形式),并且可表示的最近相邻值之间的差异将至少是 pi/2 倍 DBL_EPSILON,大致为 3e-16(或其他浮点类型的相应值)。对于 pi/2 的一些奇数倍,您可能会“走运”并发现它非常靠近两个邻居之一,但平均而言,您会发现它大约在 1e-16 之外。所以你的输入1e-16 左右已经错了

现在,余弦在其零点处具有斜率 +1 或 -1,因此输出中的误差将与输入中的误差大致成比例。但是要得到一个精确的零,您需要小于最小的可表示非零双精度的误差,大约是2e-308。这比输入中的误差小了近 300 个数量级。

虽然您在理论上可以“走运”并且如果 pi/2 确实非常接近于最接近的可表示双精度数,但仅将其建模为随机的可能性很小。我相信甚至有证据表明x 的正确舍入值是精确为零的没有双重。对于单精度 (float),这可以通过蛮力轻松确定;对于double,这可能也是可行的,但计算量很大。

至于为什么printf 打印-0.000000,只是%f 的默认值是小数点后6 位,远远不足以看到第一个有效数字。使用 %e%g,可选地使用大精度修饰符,将向您显示您得到的结果的近似值,它实际上保留了一些意义,并让您了解您的结果是否良好。

【讨论】:

    【解决方案2】:

    我的问题是,当我输入 90 时,我得到的答案是 -0.000000。 (为什么我得到 -0.000 而不是 0.000?)

    • cosine(90) 不够精确,导致值为 0.0。使用printf("cosine(%d)= %le\n",y, cosine(y));(注意e)查看结果的更多信息视图。相反,cosine(90) 在 [-0.0005 ... -0.0] 范围内生成否定结果,并且 四舍五入"-0.000" 以进行打印。

    谁能解释我为什么以及如何解决这个问题?

    OP 的 cosine() 缺乏足够的范围缩减,对于 degrees 可能是exact

    x %= 360; 是一个很好的第一步,但可以将范围缩小到 90° 宽度,例如 [-45°...45°]、[45°...135°] 等。

    还建议:使用具有足够项(例如 10)的泰勒级数和良好的机器 PI1。比pow(rad, 2 * n) / fact(2 * n) 更仔细地形成术语,这会注入过多的错误。

    Example1, example2.

    可能的其他改进,但需要启动 OP。


    1#define PI 3.1415926535897932384626433832795

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-10
      • 2017-06-06
      • 1970-01-01
      • 2017-12-02
      • 2016-06-16
      • 2015-06-20
      • 1970-01-01
      相关资源
      最近更新 更多