【问题标题】:Calculating the value of pi-what is wrong with my code计算 pi 的值-我的代码有什么问题
【发布时间】:2009-09-25 19:59:08
【问题描述】:

我正在做另一个 C++ 练习。我必须从无穷级数中计算 pi 的值:

pi=4 - 4/3 + 4/5 - 4/7 + 4/9 -4/11+ 。 . .

程序必须在本系列的前 1,000 个术语之后打印 pi 的近似值。 这是我的代码:

#include <iostream>
using namespace std;

int main()
{
    double pi=0.0;
    int counter=1;

    for (int i=1;;i+=2)//infinite loop, should "break" when pi=3.14159
    {
        double a=4.0;
        double b=0.0;

        b=a/static_cast<double>(i);

        if(counter%2==0)
            pi-=b;
        else
            pi+=b;

        if(i%1000==0)//should print pi value after 1000 terms,but it doesn't  
            cout<<pi<<endl;

        if(pi==3.14159)//this if statement doesn't work as well
            break;

        counter++;
    }

    return 0;
}

它编译时没有错误和警告,但执行后只出现空的控制台窗口。如果我删除行“if(i%1000==0)”,我可以看到它确实运行并打印了每个 pi 值,但它并没有停止,这意味着第二个 if 语句也不起作用。我不知道还能做什么。我假设这可能是一个简单的逻辑错误。

【问题讨论】:

  • 使用浮点规则 #2 编程:永远不要比较非整数浮点值是否相等。
  • @RBarryYoung:什么是规则#1? :)

标签: c++ math pi


【解决方案1】:

好吧,i % 1000 永远不会 = 0,因为您的计数器从 i = 1 开始运行,然后以 2 为增量。因此,i 始终是奇数,并且永远不会是 1000 的倍数。

它永远不会终止的原因是该算法不能精确地收敛到 3.14157 - 无论是低于或高于近似值,它都会具有更高的精度。你想说“当在 3.14157 的给定增量内时”,所以写

if (fabs(pi - 3.14157) < 0.001)
  break

或类似的东西,无论您想在停止之前获得“接近”的程度。

【讨论】:

  • 我几乎同意你的所有回答。但是,您应该将该行代码更改为“if (fabs(pi - 3.14157)
  • 感谢您的评论,jprete - 愚蠢的错误,我已经更改了示例:)
  • 我已经添加了 if (fabs(pi - 3.14157) ,但只出现了一个空控制台。
  • 如果您在每次迭代时打印该值(即删除 if),它实际上是否会收敛到 |pi - 3.14157| 的值?小于您的增量?
  • Nitpick:我假设您的意思是 3.14159,而不是 3.14157。
【解决方案2】:

由于 i 从 1 开始并以 2 递增,因此 i 始终是奇数,因此 i % 1000 永远不会为 0。

【讨论】:

    【解决方案3】:

    你有不止一个问题:

    A. i%1000==0 永远不会是真的,因为你只迭代奇数。

    B. pi==3.14159 :你不能像这样比较双精度值,因为浮点数的表示方式(你可以在另一个问题中阅读它)。为了让它工作,您应该以另一种方式比较这些值 - 一种方法是将它们彼此相减并检查绝对结果是否低于 0.0000001。

    【讨论】:

    • @Pat:在这里说“谢谢”的最佳方式是为答案投票。
    【解决方案4】:
    1. 您有浮点精度问题。试试if(abs(pi - 3.14159) &lt; 0.000005)
    2. i%1000 永远不会为 0,因为 i 总是奇数。

    【讨论】:

      【解决方案5】:

      不应该是:

      if (counter%1000==0)
      

      【讨论】:

      • 但这会导致它每 500 步停止,而不是每 1000 步
      【解决方案6】:
      1. i 从 1 开始,然后递增 2。因此 i 始终是奇数,永远不会是 1000 的倍数,这就是为什么 if (i % 1000 == 0) 永远不会通过的原因。

      2. 由于浮点精度问题,无法直接比较浮点数。您需要比较这些值之间的差异是否足够接近。

      【讨论】:

        【解决方案7】:

        pi=4 - 4/3 + 4/5 - 4/7 + 4/9 -4/11 + ...

        概括

        pi = Σi=0 (-1)i 4 / (2i+1)

        这为我们提供了一种更简洁的方法来处理每个术语;第 i 项由下式给出:

        double term = pow(-1,i%2) * 4 / (2*i+1);
        

        其中 i=0,1,2,...,N

        因此,给定一些迭代次数 N,我们的循环可以相当简单

        int N=2000;
        double pi=0;
        for(int i=0; i<N; i++)
        {
            double term = pow(-1,i%2) * 4 / (2*(double)i+1);
            pi += term;
            cout << i << "\t" << pi <<endl;
        }
        

        您最初的问题是“程序必须在本系列的前 1,000 个术语之后打印 pi 的近似值”。这并不意味着需要检查是否已达到 3.14159,因此我没有在此处包含此内容。 pow(-1,i%2) 调用只是为了避免 if 语句(很慢)并防止大 i 的任何并发症。

        请注意,经过多次迭代后,pi 的大小与校正项的大小(例如 -4/25)之间的差异会非常小,以至于超出 double 的精度,所以你需要更高精度的类型来处理它。

        【讨论】:

          【解决方案8】:

          默认情况下,abs 使用用于 int 的 abs 宏。对于双打,请使用 cmath 库。

          #include <iostream>
          #include <cmath>
          
          int main()
          {
              double pi=0.0;
          
              double a=4.0;
              int i = 1; 
          
              for (i=1;;i+=2)
              {
          
                  pi += (1 - 2 * ((i/2)%2)) * a/static_cast<double>(i);          
          
                  if( std::abs(pi - 3.14159) < 0.000001 )
                        break;
          
                  if (i > 2000) //1k iterations
                        break;
              }
          
              std::cout<<pi<<std::endl;
          
              return 0;
          }
          

          【讨论】:

          • 我认为语句 if (i>2000) break;是不正确的,因为它在达到 3.14159(超过 2000 次迭代)之前中断了循环。使用 if 语句添加计数器变量 if (counter%1000==0) 应该可以修复它。我将发布更正后的代码。
          • 您是正确的, i > 2k 在 3.14159 之前中断并且并不真正属于。在他最初的实现中,他进行了两次检查,但都没有奏效。所以,我把它作为一个例子。
          【解决方案9】:

          这是更正后的代码。我认为如果将来有人遇到类似问题,它可能会有所帮助。

          #include <iostream>
          #include <cmath>
          
          using namespace std;
          
          int main()
          {
          double pi=0.0;
          int counter=1;
          
          for (int i=1;;i+=2)
          {
           double a=4.0;
           double b=0.0;
          
           b=a/static_cast<double>(i);
          
           if(counter%2==0)
            pi-=b;
           else
            pi+=b;
          
           if(counter%1000==0) 
            cout<<pi<<" "<<counter<<endl;
          
          
           if (fabs(pi - 3.14159) < 0.000001) 
            break;
          
           counter++;
          }
          cout<<pi;
          
           return 0;
          }
          

          【讨论】:

            【解决方案10】:

            这里有一个更好的:

            class pi_1000
            {
            public:
                double doLeibniz( int i ) // Leibniz famous formula for pi, source: Calculus II :)
                {
                    return ( ( pow( -1, i ) ) * 4 ) / ( ( 2 * i ) + 1 );
                }
            
             void piCalc()
            {
                double pi = 4;
                int i;
            
                cout << "\npi calculated each iteration from 1 to 1000\n"; //wording was a bit confusing.
                                                                //I wasn't sure which one is the right one: 0-1000 or each 1000th.
                for( i = 1; i < 1000; i++ )
                {
                    pi = pi + doLeibniz( i );
                    cout << fixed << setprecision( 5 ) << pi << "\t" << i + 1 << "\n";
                }
            
                pi = 4;
                cout << "\npi calculated each 1000th iteration from 1 to 20000\n";
                for( i = 1; i < 21000; i++ )
                {
                    pi = pi + doLeibniz( i );
                    if( ( ( i - 1 ) % 1000 )  == 0 )
                        cout << fixed << setprecision( 5 ) << pi << "\t" << i - 1 << "\n";
                }
            
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-12-03
              • 1970-01-01
              • 1970-01-01
              • 2011-07-28
              相关资源
              最近更新 更多