Как запретить программе возвращать «inf»?

Я работаю над знаменитой задачей классической физики: - задачей трёх тел. Я составил следующую программу: -

#include <math.h>
#include <iostream>
using namespace std;

class System_variables
{
    private:

        // These are the system variables:- position, velocity and accleration
        long double body_1_pos[3];
        long double body_2_pos[3];
        long double body_3_pos[3];

        long double body_1_v[3];
        long double body_2_v[3];
        long double body_3_v[3];

        long double acc_body_1[3];
        long double acc_body_2[3];
        long double acc_body_3[3];

        // Needed constants
        const long double body_mass = 6 * pow(10, 24);
        double time_step;
        const long double G = 6.67 * pow(10, -11);

    public:


        System_variables(long double body_1_pos_i[3], long double body_2_pos_i[3], long double body_3_pos_i[3], long double body_1_v_i[3], long double body_2_v_i[3], long double body_3_v_i[3])
        {
            // Initiates the system variables, position and velocity.
            for (int m = 0; m<=2; m++)
            {
                body_1_pos[m] = body_1_pos_i[m];
                body_2_pos[m] = body_2_pos_i[m];
                body_3_pos[m] = body_3_pos_i[m];

                body_1_v[m] = body_1_v_i[m];
                body_2_v[m] = body_2_v_i[m];
                body_3_v[m] = body_3_v_i[m];
            }
        }
        void init_time_step(double step)
        {
            // Initializes time step.
            time_step = step;
        }
        void calculate_acc()
        {   
            // Calculates the accleration on the bodies using newtons equations.
            for (int m=0; m<=2; m++)
            {
                acc_body_1[m] = G * body_mass/pow((body_2_pos[m] - body_1_pos[m]), 2) + G * body_mass/pow((body_3_pos[m] - body_1_pos[m]), 2);
                acc_body_2[m] = G * body_mass/pow((body_1_pos[m] - body_2_pos[m]), 2) + G * body_mass/pow((body_3_pos[m] - body_2_pos[m]), 2);
                acc_body_3[m] = G * body_mass/pow((body_1_pos[m] - body_3_pos[m]), 2) + G * body_mass/pow((body_2_pos[m] - body_3_pos[m]), 2);
            };
            return;
        }

        void apply_acc_to_v()
        {   
            // Applys the accleration to the velocities of the bodies.
            for (int m=0; m<=2; m++)
            {
                body_1_v[m] += acc_body_1[m] * time_step;
                body_2_v[m] += acc_body_2[m] * time_step;
                body_3_v[m] += acc_body_3[m] * time_step;
            }
            return;
        }

        void apply_v_to_pos()
        {   
            // Applys the velocities to the positions of the bodies.
            for (int m=0; m<=2; m++)
            {
                body_1_pos[m] += body_1_v[m] * time_step;
                body_2_pos[m] += body_2_v[m] * time_step;
                body_3_pos[m] += body_3_v[m] * time_step;
            }
            return;
        }

        long double get_body_1_pos(int index)
        {
            // Returns the position of body 1 based on index.
            return body_1_pos[index];
        }

        long double get_body_2_pos(int index)
        {   
            // Returns the position of body 2 based on index.
            return body_2_pos[index];
        }

        long double get_body_3_pos(int index)
        {
            // Returns the position of body 3 based on index.
            return body_3_pos[index];
        }

        long double get_body_1_v(int index)
        {
            // Returns the velocity of body 1 based on index.
            return body_1_v[index];
        }

        long double get_body_2_v(int index)
        {
            // Returns the velocity of body 2 based on index.
            return body_2_v[index];
        }

        long double get_body_3_v(int index)
        {
            // Returns the velocity of body 3 based on index.
            return body_3_v[index];
        }


};

int main()
{
    long double array_1[3] = {100,100,100}; 
    long double array_2[3]  = {10000000000, 10000000000, 10000000000};
    long double array_3[3] = {10000000000, 10000000000, -10000000000};
    long double array_0[3] =  {0,0,0};

    System_variables system(array_1, array_2, array_3, array_0, array_0, array_0);
    system.init_time_step(100);

    for (int time = 0; time <= 10; time++)
    {
        cout << "x position of body 1: " << system.get_body_1_pos(0) << endl 
        << "y position of body 1: " << system.get_body_1_pos(1) << endl 
        << "z position of body 1: " << system.get_body_1_pos(2) << endl << endl;

        cout << "x position of body 2: " << system.get_body_2_pos(0) << endl 
        << "y position of body 2: " << system.get_body_2_pos(1) << endl 
        << "z position of body 2: " << system.get_body_2_pos(2) << endl << endl;

        cout << "x position of body 3: " << system.get_body_3_pos(0) << endl 
        << "y position of body 3: " << system.get_body_3_pos(1) << endl 
        << "z position of body 3: " << system.get_body_3_pos(2) << endl << endl;

        system.calculate_acc();
        system.apply_acc_to_v();
        system.apply_v_to_pos();
    }
}

Следует отметить, что вместо использования классического типа данных double я использовал long double, потому что, когда я использовал тип данных double, через некоторое время выводился inf, поэтому я использовал long double, думая, что он будет хранить десятичные дроби в больших размерах. точность. Но это не сработало. Я тоже пробовал float (хотя точность меньше), но результат всегда один и тот же: -

x position of body 1: 100
y position of body 1: 100
z position of body 1: 100

x position of body 2: 1e+10
y position of body 2: 1e+10
z position of body 2: 1e+10

x position of body 3: 1e+10
y position of body 3: 1e+10
z position of body 3: -1e+10

x position of body 1: 100.08
y position of body 1: 100.08
z position of body 1: 100.08

x position of body 2: inf
y position of body 2: inf
z position of body 2: 1e+10

x position of body 3: inf
y position of body 3: inf
z position of body 3: -1e+10

x position of body 1: 100.16
y position of body 1: 100.16
z position of body 1: 100.24

x position of body 2: -nan(ind)
y position of body 2: -nan(ind)
z position of body 2: 1e+10

x position of body 3: -nan(ind)
y position of body 3: -nan(ind)
z position of body 3: -1e+10

x position of body 1: -nan(ind)
y position of body 1: -nan(ind)
z position of body 1: 100.48

x position of body 2: -nan(ind)
y position of body 2: -nan(ind)
z position of body 2: 1e+10

x position of body 3: -nan(ind)
y position of body 3: -nan(ind)
z position of body 3: -1e+10

Я подумываю об использовании указателей, но сомневаюсь, что это правильная идея. Что вы думаете?

ОТ: float имеет меньшую точность, чем double. И нет никакой гарантии, что long double на самом деле больше double. И long long, конечно, не может хранить значения с плавающей запятой. И нет, указатели не помогут.

Some programmer dude 19.05.2024 08:53

Что касается вашей проблемы, пробовали ли вы ее отладить ? Например, используя отладчик для пошагового выполнения кода, отслеживая переменные и их значения.

Some programmer dude 19.05.2024 08:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
116
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Ваши расчеты не являются численно стабильными.

Первое место, где в вашей программе начинаются сбои, — это расчет ускорения. Например:

acc_body_2[m] = G * body_mass/pow((body_1_pos[m] - body_2_pos[m]), 2) + 
                G * body_mass/pow((body_3_pos[m] - body_2_pos[m]), 2);

Body_3 и Body_2 содержат очень большие числа одинаковой величины. Их разница будет очень небольшой. Возведение в квадрат этого небольшого числа делает его еще меньше. Затем вы делите массу на это очень маленькое число, получая очень, очень большое число. Это не хорошо.

В вашем случае числа для body_3 и body_2 даже идентичны, поэтому вы действительно делите здесь на 0, что отправляет вас прямо к inf. Но даже если числа лишь отчасти равны, вы, скорее всего, столкнетесь здесь с проблемами.

Обратите внимание, что это в первую очередь не проблема ограниченной точности используемого типа данных с плавающей запятой (хотя проблему можно в некоторой степени смягчить за счет увеличения точности), а в первую очередь проблема способа структурирования вычислений. Существует целая дисциплина информатики, называемая численными вычислениями, посвященная решению тонких проблем, которые могут возникнуть при подобных вычислениях.

Я бы посоветовал вам начать с небольшого исследования численного интегрирования, что на самом деле вы и пытаетесь сделать здесь, и взглянуть на некоторые распространенные ошибки, такие как проблема катастрофического отмены.

Спасибо, сэр, я понял и проведу необходимое исследование.

Ronny 19.05.2024 09:30

Ваш calculate_acc не только численно нестабилен: он совершенно неверен.

// X,Y,Z coordinates of the acceleration on "body1" caused by "body2"'s gravity according to OP
accelX = G * body_mass/pow((body_2_pos[0] - body_1_pos[0]), 2);
accelY = G * body_mass/pow((body_2_pos[1] - body_1_pos[1]), 2);
accelZ = G * body_mass/pow((body_2_pos[2] - body_1_pos[2]), 2);

Вектор ускорения body1, вызванный силой тяжести body2, согласно закону гравитации Ньютона, равен:

// PSEUDOCODE
// p1 -> body_1_pos: vector position of body1
// p2 -> body_2_pos: vector position of body2
// m2: mass of body2
// norm(someVector): euclidian norm of someVector

accelerationNorm = G * m2 / (norm(p2 - p1))²; // 1d scalar, dimension: acceleration
unitVector = (p2 - p1) / norm(p2 - p1); // 3d vector, dimension: one
forceVector = accelerationNorm * unitVector; // 3d vector, dimension: acceleration

Обратите внимание на norm(p2 - p1), который почему-то никогда не вычисляется в вашей программе. Вы не можете просто разделить на (p2[0]-p1[0])², чтобы вычислить координату X, и просто (p2[1]-p1[1])², чтобы вычислить координату Y. Сначала вы должны вычислить правильную норму: sqrt(x² + y² + z²), затем использовать это значение для вычисления каждой координаты вектора ускорения.

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