Вычисление значения pi -что не так с моим кодом

StackOverflow https://stackoverflow.com/questions/1479291

  •  18-09-2019
  •  | 
  •  

Вопрос

Я выполняю еще одно упражнение на C ++.Я должен вычислить значение числа пи из бесконечного ряда:

пи=4 - 4/3 + 4/5 – 4/7 + 4/9 -4/11+ ...

Программа должна вывести приблизительное значение числа пи после каждого из первых 1000 слагаемых этого ряда.Вот мой код:

#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 также не работает.Я не уверен, что еще можно сделать.Я предполагаю, что это, вероятно, простая логическая ошибка.

Это было полезно?

Решение

Ну, i % 1000 никогда не будет = 0, так как ваш счетчик запускается с i = 1, затем с шагом 2.Следовательно, i всегда нечетно и никогда не будет кратно 1000.

Причина, по которой он никогда не завершается, заключается в том, что алгоритм не сходится точно к 3.14157 - это будет более высокая точность либо при меньшем, либо при большем приближении.Вы хотите сказать "Когда в пределах заданной дельты 3,14157", поэтому напишите

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

или что-то подобное, как бы "близко" вы ни хотели подойти, прежде чем остановитесь.

Другие советы

Поскольку вы начинаете i с 1 и увеличиваете на 2, i всегда является нечетным числом, поэтому i % 1000 никогда не будет равен 0.

у вас больше одной проблемы:

A.i%1000==0 никогда не будет истинным, потому что вы повторяете только нечетные числа.

B.число пи==3,14159 :вы не можете сравнивать двойные значения просто так, потому что способ представления чисел с плавающей запятой (вы можете прочитать об этом здесь, в другом вопросе).чтобы это сработало, вы должны сравнить значения другим способом - один из способов - вычесть их друг из друга и проверить, что абсолютный результат меньше 0,0000001.

  1. У вас есть проблемы с точностью до плавающей запятой.Попробуй if(abs(pi - 3.14159) < 0.000005).
  2. i%1000 никогда не будет 0, потому что i это всегда странно.

Разве это не должно быть:

if (counter%1000==0)
  1. i начинается с 1, а затем увеличивается на 2.Следовательно, i всегда нечетно и никогда не будет кратно 1000, вот почему if (i % 1000 == 0) никогда не проходит.

  2. Прямое сравнение значений с плавающей точкой не работает из-за проблем с плавающей точностью.Вам нужно будет сравнить, чтобы разница между значениями была достаточно близкой.

пи=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;
}

В вашем первоначальном вопросе говорилось: "Программа должна напечатать приблизительное значение pi после каждого из первых 1000 терминов этой серии".Это не подразумевает никакой необходимости проверять, был ли достигнут уровень 3.14159, поэтому я не включил это сюда.В pow(-1,i%2) звонок - это просто для того, чтобы избежать if заявления (которые выполняются медленно) и предотвращают любые осложнения с большими i.

Имейте в виду, что после ряда итераций разница между величиной pi и величиной корректирующего члена (скажем, -4 / 25) будет настолько мала, что превысит точность a double, так что вам понадобятся типы более высокой точности, чтобы справиться с этим.

По умолчанию abs использует макрос abs, который предназначен для int.Для удвоения используйте библиотеку 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;
}

Вот исправленный код.Я подумал, что это может быть полезно в будущем, если у кого-то возникнет подобная проблема.

#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;
}

Вот один из них получше:

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";
    }

}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top