【问题标题】:why floating point numbers does not give desired answer?为什么浮点数没有给出想要的答案?
【发布时间】:2021-03-23 22:32:35
【问题描述】:

嘿,我正在制作小型 C++ 程序来计算 sin(x) 的值直到小数点后 7 位,但是当我使用这个程序计算 sin(PI/2) 时,它给了我0.9999997 而不是比1.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);
    }
}

【问题讨论】:

  • @user207421:请不要随意关闭浮点问题作为该问题的重复项。这里的问题是由于整数运算中的错误; int factorial(int) 函数溢出,并将其更改为 double factorial(int) 会导致程序产生所需的输出“1”。由于浮点行为而得出包含浮点代码的程序错误的结论是错误的。

标签: c++ floating-point precision floating-accuracy


【解决方案1】:

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

对于[-pi/4...+pi/4] 之外的值,Taylor 的 sin/cos 级数收敛缓慢,并遭受到项的取消和 int factorial(int n)** 的溢出。留在甜蜜的范围内。

考虑使用三角属性sin(x + pi/2) = cos(x)sin(x + pi) = -sin(x) 等将x 带入[-pi/4...+pi/4] 范围。

代码使用remquo (ref2) 来查找余数和商的一部分

// 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 long 而不是int 来略微扩展int factorial(int n) 的有用范围,但这只会增加一些x。可以使用double

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

【讨论】:

  • 能否给个解释或者提供一个链接来了解remquo的功能...
  • 另见std::remquo
  • @MSalters 添加了其他好的参考。
【解决方案2】:

我看到了三个错误:

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

  • 如果t 仍然很大但为负,您的测试t &gt; 10e-7 将变为假,并退出循环。你可能想要abs(t) &gt; 1e-7

  • 要获得所需的精度,您需要达到n = 7,它需要计算factorial(13),它会溢出一个32 位int。 (如果使用 gcc,您可以使用 -fsanitize=undefined-ftrapv 捕获此问题。)您可以使用至少 64 位的 long long intint64_t 获得一些喘息空间。

【讨论】:

  • 我不明白第二个错误。为什么它会给出负值?
  • @setller:对不起,我说反了,现在修好了。
  • 只是因为这个原因我连任两个学期。
  • @setller:x 很大时没有帮助。例如,想想x=5,前两项之和是一个很大的负数。我同意这不会对x = PI/2 造成问题,但这仍然是一个错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-20
  • 1970-01-01
  • 2016-04-13
  • 1970-01-01
  • 1970-01-01
  • 2021-11-18
  • 1970-01-01
相关资源
最近更新 更多