简体   繁体   中英

How can I make this expression involving floating-point functions a compile-time constant?

I have a constant integer, steps, which is calculated using the floor function of the quotient of two other constant variables. However, when I attempt to use this as the length of an array, visual studio tells me it must be a constant value and the current value cannot be used as a constant. How do I make this a "true" constant that can be used as an array length? Is the floor function the problem, and is there an alternative I could use?

const int simlength = 3.154*pow(10,7);
const float timestep = 100;
const int steps = floor(simlength / timestep);

struct body bodies[bcount];

struct body {
    string name;
    double mass;
    double position[2];
    double velocity[2];
    double radius;
    double trace[2][steps];
};

It is not possible with the standard library's std::pow and std::floor function, because they are not constexpr -qualified.

You can probably replace std::pow with a hand-written implementation my_pow that is marked constexpr . Since you are just trying to take the power of integers, that shouldn't be too hard. If you are only using powers of 10, floating point literals may be written in the scientific notation as well, eg 1e7 , which makes the pow call unnecessary.

The floor call is not needed since float / double to int conversion already does flooring implicitly. Or more correctly it truncates , which for positive non-negative values is equivalent to flooring.

Then you should also replace the const with constexpr in the variable declarations to make sure that the variables are usable in constant expressions:

constexpr int simlength = 3.154*my_pow(10,7); // or `3.154e7`
constexpr float timestep = 100;
constexpr int steps = simlength / timestep;

Theoretically only float requires this change, since there is a special exception for const integral types, but it seems more consistent this way.

Also, I have a feeling that there is something wrong with the types of your variables. A length and steps should not be determined by floating-point operations and types, but by integer types and operations alone. Floating-point operations are not exact and introduce errors relative to the mathematical precise calculations on the real numbers. It is easy to get unexpected off-by-one or worse errors this way.

You cannot define an array of a class type before defining the class.

Solution: Define body before defining bodies .


Furthermore, you cannot use undefined names.

Solution: Define bcount before using it as the size of the array.


Is the floor function the problem, and is there an alternative I could use?

std::floor is one problem. There's an easy solution: Don't use it. Converting a floating point number to integer performs similar operation implicitly (the behaviour is different in case of negative numbers).

std::pow is another problem. It cannot be replaced as trivially in general, but in this case we can use a floating point literal in scientific notation instead.

Lastly, non-constexpr floating point variable isn't compile time constant. Solution: Use constexpr .

Here is a working solution:

constexpr int simlength = 3.154e7;
constexpr float timestep = 100;
constexpr int steps = simlength / timestep;

PS trace is a very large array. I would recommend against using so large member variables, because it's easy for the user of the class to not notice such detail, and they are likely to create instances of the class in automatic storage. This is a problem because so large objects in automatic storage are prone to cause stack overflow errors. Using std::vector instead of an array is an easy solution. If you do use std::vector , then as a side effect the requirement of compile time constant size disappear and you will no longer have trouble using std::pow etc.

Because simlength is 3.154*10-to-the-7th , and because timestep is 10-squared, then the steps variable's value can be written as:

3.154e7 / 1e2 == 3.154e5

And, adding a type-cast, you should be able to write the array as:

double trace[2][(int)(3.154e5)];

Note that this is HIGHLY IRREGULAR, and should have extensive comments describing why you did this.

Try switching to constexpr :

constexpr int   simlength = 3.154e7;
constexpr float timestep  = 1e2;
constexpr int   steps     = simlength / timestep;

struct body {
    string name;
    double mass;
    double position[2];
    double velocity[2];
    double radius;
    double trace[2][steps];
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM