为什么浮点数不能给出想要的答案?

why floating point numbers does not give desired answer?

提问人:setller 提问时间:3/23/2021 更新时间:3/23/2021 访问量:179

问:

嘿,我正在制作小型 C++ 程序来计算 sin(x) 的值直到小数点后 7 位,但是当我使用此程序计算 sin(PI/2) 时,它给了我而不是我如何解决这个错误? 我知道为什么我要得到这个值作为输出,问题是我应该采用什么方法来解决这个逻辑错误?0.99999971.0000000

这是我的代码供参考

#include <iostream>
#include <iomanip>
#define PI 3.1415926535897932384626433832795
using namespace std;

double sin(double x);
int factorial(int n);
double Pow(double a, int b);

int main()
{
    double x = PI / 2;
    cout << setprecision(7)<< sin(x);
    return 0;
}

double sin(double x)
{
    int n = 1;      //counter for odd powers.
    double Sum = 0; // to store every individual expression.
    double t = 1;   // temp variable to store individual expression
    for ( n = 1; t > 10e-7; Sum += t, n = n + 2)
    {
        // here i have calculated two terms at a time because addition of two consecutive terms is always less than 1.
        t = (Pow(-1.00, n + 1) * Pow(x, (2 * n) - 1) / factorial((2 * n) - 1))
            +
            (Pow(-1.00, n + 2) * Pow(x, (2 * (n+1)) - 1) / factorial((2 * (n+1)) - 1));
    }

    return Sum;
}
int factorial(int n)
{
    if (n < 2)
    {
        return 1;
    }
    else
    {
        return n * factorial(n - 1);
    }
}
double Pow(double a, int b)
{
    if (b == 1)
    {
        return a;
    }
    else
    {
        return a * Pow(a, b - 1);
    }
}
C++ 浮点 精度

评论

2赞 Eric Postpischil 3/23/2021
@user207421:请不要将浮点问题混淆为该问题的重复项。这里的问题是由于整数算术中的错误;函数溢出,将其更改为会导致程序生成所需的输出“1”。由于浮点行为而得出结论,认为包含浮点代码的程序是错误的,这是错误的。int factorial(int)double factorial(int)

答:

1赞 Nate Eldredge 3/23/2021 #1

我看到三个错误:

  • 10e-7它似乎比你想要的大 10 倍。我想你想要.10*10^(-7)1e-7

  • 您的测试将变为错误,如果仍然很大但为阴性,则退出循环。你可能想要.t > 10e-7tabs(t) > 1e-7

  • 为了获得所需的精度,您需要达到 ,这让你计算 ,它溢出了 32 位。(如果使用 gcc,则可以使用 或 来捕获它。您可以使用至少 64 位或 .n = 7factorial(13)int-fsanitize=undefined-ftrapvlong long intint64_t

评论

0赞 setller 3/23/2021
我不明白第二个错误。为什么它会给出负值?
0赞 Nate Eldredge 3/23/2021
@setller:对不起,我倒过来说了,现在修好了。
0赞 setller 3/23/2021
只是因为这个原因,我连续上了两个学期。
0赞 Nate Eldredge 3/23/2021
@setller:大了也无济于事。例如,想想前两项的总和是一个很大的负数。我同意它不会碰巧引起问题,但它仍然是一个错误。xx=5x = PI/2
1赞 chux - Reinstate Monica 3/23/2021 #2

sin(PI/2) ...它给了我 0.9999997 而不是 1.0000000

对于泰勒正弦/余弦级数之外的值收敛缓慢,并且会受到项的取消和 ** 溢出的影响。保持在甜蜜的范围内。[-pi/4...+pi/4]int factorial(int n)

考虑使用 trig 属性 、 等来引入范围。sin(x + pi/2) = cos(x)sin(x + pi) = -sin(x)x[-pi/4...+pi/4]

代码使用 remquoref2) 来查找商的余数和部分

// Bring x into the -pi/4 ... pi/4  range (i.e. +/- 45 degrees)
// and then call owns own sin/cos function.
double my_wide_range_sin(double x) {
  if (x < 0.0) {
    return -my_sin(-x);
  }
  int quo;
  double x90 = remquo(fabs(x), pi/2, &quo);
  switch (quo % 4) {
    case 0:
      return sin_sweet_range(x90);
    case 1:
      return cos_sweet_range(x90);
    case 2:
      return sin_sweet_range(-x90);
    case 3:
      return -cos_sweet_range(x90);
  }
  return 0.0;
}

这意味着 OP 也需要编写一个函数。cos()


**可以用来代替略微扩展的有用范围,但那只能增加一些。可以使用.long longintint factorial(int n)xdouble

更好的方法根本不会使用,而是按或类似方式缩放每个连续的术语。factorial()1.0/(n * (n+1))

评论

0赞 setller 3/23/2021
您能否给出解释或提供链接来了解 remquo 的功能......
0赞 MSalters 3/23/2021
参见 std::remquo
0赞 chux - Reinstate Monica 3/23/2021
@MSalters 添加了额外的良好参考。