Приведение типов С++ от double к const int работает неправильно

У меня есть переменная типа const int, но параметры, от которых она зависит, имеют тип double. Когда я пытаюсь преобразовать это из «двойного» в «константное целое», это не работает должным образом. Например, когда N должно быть 991, оно вводится как 990. Я испробовал несколько методов, и только один сработал, но я не уверен, что этот метод будет работать постоянно. Вот некоторые методы, которые я пробовал:

Первый метод:

const int N = (Ls-1)/dx + 1;

Второй метод:

const int N = static_cast<const int>((Ls-1)/dx) + 1;

Третий метод:

double Z = (Ls-1)/dx + 1;
const int N = Z;

Четвертый метод (единственный рабочий метод):

double Z = (Ls-1)/dx;
const int N = Z + 1;

Обратите внимание, что dx — это такое значение, что остаток от (Ls-1)/dx всегда будет равен нулю (т. е. это всегда целочисленное значение). Могу ли я в любом случае объяснить, почему другие методы не работают, чтобы я мог лучше понять приведение типов?

РЕДАКТИРОВАТЬ: В соответствии с просьбой я загружаю весь код, чтобы показать, как все работает:

#include <iostream>
#include <math.h>
#include <stdio.h>
#include <fstream>
#include <cmath>
#include <algorithm>

#define pi 3.14159265

using namespace std;

//Define Fluid Properties
double rho_L = 998; //Liquid Density
double rho_LG = 828.9; //Liquid-Gas Density Ratio
double mu_L = 0.000798; //Liquid Viscosity
double mu_LG = 40.24; //Liquid-Gas Viscosity Ratio
double sigma = 0.0712; //Surface Tension
double nu_G = (mu_L/mu_LG)/(rho_L/rho_LG);

//Define Injector Properties
double Uinj = 56.7; //Injection Velocity
double Dinj = 0.0998; //Injector Diameter
double theta = 15.0*pi/180.0; //Spray Cone Angle
double L = 500.0*Dinj; //Atomization Length
double Ls = L/Dinj; //Normalized Atomization Length

//Define Solver Parameters
double K = 5294; //Viscous Dissipation Coefficient
double Eps = pow(10,-5); //Residual Error
double dx = 0.0001; //Step Size
double Ui = 10; //Initial Guess
//const int Z = static_cast<const int>((Ls-1)/dx + 1) + 1;
const int N = (Ls-1)/dx + 1;//Z;

double deriv (double U, double X, double delta, double m)
{
    double dudx;
    dudx = -(1.0/delta)*(1.0/U)*(U - sqrt(1.0 - U)/sqrt(m*X*X))*(U - sqrt(1.0 - U)/sqrt(m*X*X));
    return (dudx);
}

int main()
{
    //Declare Variables
    int max_step;
    double ERR;
    int step;
    double DEN;
    double SMD;
    double m;
    double Ug;
    double Re;
    double Cd;
    double delta;
    double K1;
    double K2;
    double K3;
    double K4;

    //Allocate Memory From Heap
    double *U = new double [N];
    double *X = new double [N];

    //Initialize Vectors and Variables
    DEN = 0.5*rho_L - (4.0/3.0)*K*(mu_L)/(Uinj*Dinj*Dinj)*L;

    m = 4.0/rho_LG*tan(theta)*tan(theta);

    for (int i = 0; i < N; i++)
    {
        X[i] = 1.0 + dx*i;
    }
    U[0] = 1.0;

    max_step = 1;
    ERR = 1;
    step = 0;
    while(abs(ERR) > Eps && step < max_step)
    {

        //Calculate Ug
        Ug = sqrt(1.0 - (Ui/Uinj))/sqrt(m*Ls*Ls)*Uinj;

        //Calculate SMD
        SMD = 6.0*sigma/(DEN*(Uinj*Uinj - Ui*Ui));

        //Calculate Re # and Drag Coefficient
        Re = abs(Ui-Ug)*SMD/nu_G;

        if(Re <= 0.01)
        {
            Cd = (0.1875) + (24.0/Re);
        }
        else if(Re > 0.01 && Re <= 260.0)
        {
            Cd = (24.0/Re)*(1.0 + 0.1315*pow(Re,(0.32 - 0.05*log10(Re))));
        }
        else
        {
            Cd = (24.0/Re)*(1.0 + 0.1935*pow(Re,0.6305));
        }

        //Determine New U
        delta = (4.0/3.0)*(1.0/Cd)*(rho_LG)*(SMD/Dinj);

        //RK4
        for (int i = 0; i < N-1; i++)
        {
            K1 = deriv(U[i],X[i],delta,m);
            K2 = deriv(U[i]+0.5*dx*K1,X[i]+0.5*dx,delta,m);
            K3 = deriv(U[i]+0.5*dx*K2,X[i]+0.5*dx,delta,m);
            K4 = deriv(U[i]+dx*K3,X[i+1],delta,m);
            U[i+1] = U[i] + dx/6.0*(K1 + 2.0*K2 + 2.0*K3 + K4);
            //if(i >= 0 && i <= 3)
                //cout << i << " " << K1 << " " << K2 << " " << K3 << " " << K4 << " " << U[i] << endl;
        }

        ERR = abs(U[N-1]*Uinj - Ui)/Ui;

        Ui = U[N-1]*Uinj;

        step = step + 1;
    }

    SMD = 6.0*sigma/(DEN*(Uinj*Uinj - Ui*Ui));

    cout << "U = " << Ui << endl;
    cout << "SMD = " << SMD << endl;
    cout << "DEN = " << DEN << endl;
    cout << "Ug = " << Ug << endl;
    cout << "m = " << m << endl;
    cout << "delta = " << delta << endl;
    cout << "Re = " << Re << endl;
    cout << "Cd = " << Cd << endl;
    cout << "U* = " << U[N-1] << endl;
    cout << "Error = " << ERR << endl;
    cout << "step = " << step << endl;

    //Output Data Into Text File
    ofstream outputdata("result-500-15.txt");
    for (int i = 0; i < N; i++)
    {
        outputdata << X[i] << " " << U[i] << '\n';
    }
    outputdata.close();

    delete [] U;
    delete [] X;

    return 0;
}

person user1562781    schedule 08.02.2013    source источник
comment
Похоже, что это может быть проблема округления - возможно, (Ls-1)/dx на самом деле (скажем) 990,999999999999, так что фактически это 991, но понижение усекает его до 990. Что произойдет, если вы попробуете static_cast<const int>((Ls-1)/dx + 0.01) + 1?   -  person ruakh    schedule 09.02.2013
comment
В настоящее время dx = 0,1, а Ls-1 = 99, поэтому (Ls-1)/dx должно быть 990, но, может быть, 0,1 в двоичном виде не может быть? Если я добавлю 0,01, это сработает, но всегда ли?   -  person user1562781    schedule 09.02.2013
comment
На самом деле, теперь, когда я думаю об этом, округление не должно быть проблемой, потому что добавление «+1» должно исправить любые проблемы с округлением.   -  person user1562781    schedule 09.02.2013
comment
+1 даст вам неправильный ответ, если Z окажется целым числом. Вам лучше использовать const int N = Z + 0,5. Однако это не удастся, если Z отрицательно. Лучше всего использовать стандартную библиотечную функцию C, roundl(), для решения подобных ситуаций.   -  person Bob Murphy    schedule 09.02.2013
comment
Я думаю, что я, возможно, неправильно описал уравнение. N = (Ls - 1)/dx + 1 - это то, что я решаю. +1 является частью уравнения. В основном я пытаюсь найти количество точек в векторе с учетом интервала (dx) и начальной и конечной точек (Ls-1).   -  person user1562781    schedule 10.02.2013


Ответы (1)


Ваша догадка верна: 0,1 не имеет конечного выражения в двоичном формате. Это довольно сложная проблема, и в ней есть много угловых случаев, которые в общем случае не будут решены добавлением 0,01, как указано в вашем комментарии. (Это сильно зависит от значений, которые вы ожидаете и т.д.)

Ваш вопрос предполагает, что частное всегда должно быть целым числом. В этом случае правильный подход для поддержания правильных результатов — не использовать double с самого начала (для Ls, dx, Z). Либо используйте дробный тип (ничего встроенного в C++, используйте свой собственный или библиотеку), десятичный тип произвольной точности (опять же, используйте библиотеку, например gmp - разумно, если вы знаете, что все ваши числа имеют завершающее десятичное выражение), или, что проще всего: если и Ls, и dx гарантированно имеют не более n цифр после запятой, умножьте оба на 10^n и используйте целочисленные типы.


Хорошо, ваш код сильно отличается от того, что я ожидал. В этом случае, на мой взгляд, правильно будет зафиксировать количество шагов N и вычислить из него dx, а не наоборот:

const int N = 10000;
double dx = (Ls-1.0)/(double)(N-1);

Если вы хотите начать со значения для dx и выбрать N таким образом, чтобы вычисленное значение для dx, спросите пользователя при запуске программы:

#include <cmath>

double dxestim;
cout << "dx should be close to: ";
cin >> dxestim;
cout << "Candidate values for N: " << endl;
int N1 = (int) floor((Ls-1)/dx + 1.0);
int N2 = (int) ceil((Ls-1)/dx + 1.0);
cout << N1 << " gives dx = " << (Ls-1.0)/(double)(N1-1) << endl;
cout << N2 << " gives dx = " << (Ls-1.0)/(double)(N2-1) << endl;
cout << "Please choose N: ";
cin >> N;
...
person us2012    schedule 09.02.2013
comment
((Ls-1)/dx + 1) дает длину массива от конечной точки (Ls) до начальной точки (1), где (dx) — расстояние между каждым элементом. Я выбираю (dx) таким образом, чтобы ((Ls-1)/dx + 1) всегда было целым числом, но, к сожалению, мне приходится иметь (Ls) и (dx) как двойные числа, потому что они используются в другом месте в таблице. код. Или лучше, чтобы я обновил их с явным приведением типов позже? - person user1562781; 10.02.2013
comment
Хм. Трудно сказать, не видя общей картины, мне непонятно, должны они быть двойными или нет. Учитывая ваше описание, как выбрать dx, чтобы частное было целым числом? Мне кажется, что для этого требуется знание того, каким должно быть целое число, чтобы вы могли сохранить его в этот момент. - person us2012; 10.02.2013
comment
Это математический решатель, поэтому dx необходимо изменить, чтобы решение было стабильным. Я выбираю его таким образом, что (Ls-1)/dx всегда будет целым числом. Я точно знаю, какое значение N должно быть заранее, но я хотел бы сделать это автоматически, чтобы мне не приходилось вводить его вручную каждый раз, когда я меняю dx. Я могу загрузить весь код, если он лучше объясняет. - person user1562781; 10.02.2013
comment
@ user1562781 Хорошо, это сильно отличается от того, что я ожидал. См. мое редактирование для того, что я считаю самым простым решением. - person us2012; 10.02.2013
comment
Да, вы должны указать N и вычислить dx исходя из этого, а не наоборот. - person Bob Murphy; 10.02.2013
comment
@ us2012 Да, это то, что я делал изначально, но я переключился на этот метод, потому что стабильность и сходимость решения сильно зависят от значения dx, поэтому было проще настроить это, чем настроить N. Думаю, я' просто придется вернуться к прежнему пути. - person user1562781; 11.02.2013
comment
@user1562781 user1562781 Но вам нужно ограничить свой dx так, чтобы N было целым числом, поэтому исправление N на самом деле не отличается от исправления dx. Если вы хотите, чтобы ваш dx был как можно ближе к заданному значению удобным способом, подумайте о том, чтобы спросить пользователя, когда программа запускается, см. редактирование выше. - person us2012; 11.02.2013
comment
@us2012 Ты прав. Это по сути то же самое. Это всего лишь небольшое неудобство, но все же лучше, чем проходить весь процесс целиком. - person user1562781; 16.02.2013