使用整数模板参数创建编译时双精度

Use integer template argument to create compiletime double

本文关键字:创建 编译 双精度 参数 整数      更新时间:2023-10-16

是否可以创建一个保存值为 1*10^x 的双精度值,其中 x 基于整数模板参数。所以像这样:

template < int exp >
struct DoubleValue
{
    static constexpr double value = ????;
}
double d = DoubleValue<20>::value; // = 1e20
double d = DoubleValue<-20>::value; // = 1e-20

由于可以用垃圾创建,因此似乎应该可以进行此类操作。

我希望在编译时评估该值(因此据我所知,std::p ow 不起作用)。此外,如果可能的话,我希望能够避免实际的迭代计算((可能是毫无根据的)对精度问题的恐惧)。我还希望能够使用较大的值作为指数,例如 200,这使得无法将值存储在标准整数类型中。

假设您的编译器支持 C++14 或更高版本(这应该是 2019 年的有效假设),使用 constexpr 函数非常简单:

constexpr double myPow(double x, int exp)
{
    double pow = 1.0;
    for (int i = 0; i < exp; ++i)
        pow *= x;
    for (int i = 0; i > exp; --i)
        pow /= x;
    return pow;
}
template < int exp >
struct DoubleValue
{
    static constexpr double value = myPow(10.0, exp);
};

请参阅此处以验证它是否有效,并且即使没有优化,该值也会在编译时生成。

根据您的用例,您甚至可能不需要 DoubleValue 结构,但可以直接使用 myPow()


更新

正如@Bob__在评论中指出的那样,关于数值精度的算法可能比这里介绍的算法更好。但自C++14以来,许多基本的语言功能都可以在constexpr函数的主体中使用。因此,只要您不需要任何外部库,您就可以自由实现适合您需求的任何算法。

如果你想要它在编译时没有std::pow,这应该这样做:

#include <iostream>
template <int e>
struct DoubleValue {
    static constexpr double value = 10.0 * DoubleValue<e - 1>::value;
};
template <>
struct DoubleValue<0> {
    static constexpr double value = 1.0;
};
int main() {
    std::cout << DoubleValue<20>::value << 'n'; //1e+20
}

C++小提琴

由于您需要在编译时可用的值,因此几乎解决它的唯一方法,我想到的是递归模板。但是,事实上,您需要根据传递值的符号来执行所述模板的不同操作,这使事情变得复杂。首先想到的是编写这样一个递归模板:

template <int exp>
struct DoubleValue
    {
    static constexpr double value = (exp < 0
                                     ? DoubleValue<exp+1>::value / 10
                                     : 10 * DoubleValue<exp-1>::value);
    };
// Default case
template <>
struct DoubleValue<0>
    {
    static constexpr double value = 1;
    };

但是,这样的解决方案不起作用,因为三元表达式的两个分支都需要解析,并且总是会导致无限递归,因为其中一个分支不会倾向于 0。然后,SFINAE浮现在脑海中:

// Base case.
template <int exp, class Enable = void>
struct DoubleValue
    {
    };
// Case when exp is positive
template <int exp>
struct DoubleValue<exp, typename std::enable_if<(exp > 0)>::type>
    {
    static constexpr double value = 10 * DoubleValue<exp-1>::value;
    };
// Case when exp is negative
template <int exp>
struct DoubleValue<exp, typename std::enable_if<(exp < 0)>::type>
    {
    static constexpr double value = DoubleValue<exp+1>::value / 10;
    };
// Default case.
template <>
struct DoubleValue<0>
    {
    static constexpr double value = 1;
    };

现场演示。