Search code examples
visual-studio-2015c++14template-meta-programmingconstexprpow

constexpr power of 10 in non-recursive way?


It is very easy to implement constexpr power of 10 in recursive way:

template<int exp, bool = (exp > 0)>
struct pow10 {
    static constexpr double value = pow10<exp - 1>::value * 10.0;
};

template<>
struct pow10<0, false> {
    static constexpr double value = 1.0;
};

template<int exp>
struct pow10<exp, false> {
    static constexpr double value = pow10<exp + 1>::value / 10.0;
};

template<int exp>
static constexpr double pow10_v = pow10<exp>::value;

static_assert(pow10_v<-3> == 1e-3, "");
static_assert(pow10_v<2> == 1e2, "");

Is it possible to make constexpr power of 10 in non-recursive way?

FYI, I'm using VS2015 which does not support relaxed-constexpr in C++14 well, thus, I cannot use for-loop in constexpr function.


Solution

  • So, if I understand correctly, you compile C++14 but your compiler isn't full compliant with C++14 constexpr functions. So you cant make a loop inside a constexpr function.

    Well... I don't have your compiler so I don't know what, exactly, your compiler doesn't support, so I propose a C++14 solution based over a non-recursive constexpr variadic template function that doesn't use a for loop. Well... two functions: one for negative powers and one for non-negative powers.

    Hoping VS2015 support it.

    The negative function is the following

    template

    constexpr T negPow10 (std::index_sequence<Is...> const &)
     {
       using unused = std::size_t[];
    
       T ret { 1 };
    
       (void)unused { 0U, (ret /= 10, Is)... };
    
       return ret;
     }
    

    The non-negative (for positive or zero powers) is almost equals but use ret *= 10 instead of ret /= 10.

    They are called through the following

    template <typename T, int E, std::size_t N = (E < 0 ? -E : E)>
    constexpr T pow10 ()
     { return E < 0
        ? negPow10<T>(std::make_index_sequence<N>{})
        : posPow10<T>(std::make_index_sequence<N>{}); }
    

    The following is a full compiling example (but observe that, as pointed by n.m., the static_assert() over the double power isn't reliable)

    #include <utility>
    
    template <typename T, std::size_t ... Is>
    constexpr T posPow10 (std::index_sequence<Is...> const &)
     {
       using unused = std::size_t[];
    
       T ret { 1 };
    
       (void)unused { 0U, (ret *= 10, Is)... };
    
       return ret;
     }
    
    template <typename T, std::size_t ... Is>
    constexpr T negPow10 (std::index_sequence<Is...> const &)
     {
       using unused = std::size_t[];
    
       T ret { 1 };
    
       (void)unused { 0U, (ret /= 10, Is)... };
    
       return ret;
     }
    
    template <typename T, int E, std::size_t N = (E < 0 ? -E : E)>
    constexpr T pow10 ()
     { return E < 0
        ? negPow10<T>(std::make_index_sequence<N>{})
        : posPow10<T>(std::make_index_sequence<N>{}); }
    
    int main ()
     {
       static_assert( pow10<long, 5>() == 1e5, "!" );
       static_assert( pow10<double, -3>() == 1e-3, "!" );
     }
    

    To be honest, this solution is (or can be) a little recursive in std::make_index_sequence.