简体   繁体   English

如何使这个涉及浮点函数的表达式成为编译时常量?

[英]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.我有一个常数 integer,步骤,它是使用其他两个常数变量的商的下限 function 计算的。 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.但是,当我尝试将其用作数组的长度时,Visual Studio 告诉我它必须是一个常量值,并且当前值不能用作常量。 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?地板 function 有问题吗?我可以使用其他替代方案吗?

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.标准库的std::powstd::floor function 是不可能的,因为它们不是constexpr限定的。

You can probably replace std::pow with a hand-written implementation my_pow that is marked constexpr .您可能可以用标记为constexpr的手写实现my_pow替换std::pow 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.如果您只使用 10 的幂,浮点文字也可以用科学记数法编写,例如1e7 ,这使得pow调用变得不必要。

The floor call is not needed since float / double to int conversion already does flooring implicitly.由于float / doubleint的转换已经隐式地进行了floor调用,因此不需要地板调用。 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替换const以确保变量在常量表达式中可用:

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.理论上只有float需要这种改变,因为const整数类型有一个特殊的例外,但这种方式似乎更一致。

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.长度长不应由浮点运算和类型决定,而应仅由 integer 类型和运算决定。 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.在定义 class 之前,您不能定义 class 类型的数组。

Solution: Define body before defining bodies .解决方案:定义body之前定义bodies


Furthermore, you cannot use undefined names.此外,您不能使用未定义的名称。

Solution: Define bcount before using it as the size of the array.解决方案:在使用之前定义bcount作为数组的大小。


Is the floor function the problem, and is there an alternative I could use?地板 function 有问题吗?我可以使用其他替代方案吗?

std::floor is one problem. std::floor是一个问题。 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).将浮点数转换为 integer 隐式执行类似的操作(在负数的情况下行为不同)。

std::pow is another problem. std::pow是另一个问题。 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.最后,非 constexpr 浮点变量不是编译时间常数。 Solution: Use constexpr .解决方案:使用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. PS trace是一个非常大的数组。 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.我建议不要使用如此大的成员变量,因为 class 的用户很容易不会注意到这些细节,而且他们很可能会在自动存储中创建 class 的实例。 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.使用std::vector代替数组是一个简单的解决方案。 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.如果您确实使用了std::vector ,那么作为副作用,编译时间常数大小的要求消失了,您将不再有使用std::pow等的问题。

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:因为simlength3.154*10-to-the-7th ,并且因为timestep是 10 平方,所以steps变量的值可以写成:

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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