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

时间:2019-01-15 09:20:47

标签: c++

是否可以创建一个保存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 :: pow无法正常工作)。 另外,如果可能的话,我希望能够避免实际的迭代计算((可能没有根据)担心精度问题)。我还希望能够使用较大的值作为指数,例如200,这样就不可能将其存储为标准整数类型。

3 个答案:

答案 0 :(得分:6)

假设您的编译器支持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);
};

请参见here以验证其是否有效,并且即使不进行优化也可以在编译时生成该值。

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


更新

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

答案 1 :(得分:1)

如果您希望在编译时不使用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++ Fiddle

答案 2 :(得分:1)

由于您需要在编译时就可以使用该值,因此解决该问题的唯一方法就是我想到的是递归模板。但是,事实上,您需要所述模板根据传递的值的签名来做不同的事情,这使事情变得复杂。首先想到的是编写这样的递归模板:

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

但是,由于以下事实,这样的解决方案将不起作用:三元表达式的两个分支都需要解析,并且这将始终导致无限递归,因为其中一个分支不会t趋于0。然后,FINA浮现在脑海:

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

Live Demo