【问题标题】:constexpr power of 10 in non-recursive way?非递归方式的 10 的 constexpr 幂?
【发布时间】:2018-06-25 16:11:48
【问题描述】:

很容易以递归方式实现constexpr 10 的幂:

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, "");

是否有可能以非递归方式使constexpr 10 的幂?

仅供参考,我正在使用 VS2015,它在 C++14 中不支持relaxed-constexpr,因此,我不能在constexpr 函数中使用 for-loop。

【问题讨论】:

  • “编译时计算”与“常量表达式”或“constexpr”不同。如果你想要一个常量表达式,那么不(不知道你为什么想要一个)。大多数现代编译器都会在编译时愉快地计算pow。请注意,无法保证 pow(10,-3) == 1e-3 在运行时或编译时计算。
  • @n.m.谢谢你。我编辑了问题以明确。
  • 必须是模板元程序吗?使用令牌粘贴创建1E&lt;power&gt; 的宏怎么样?
  • 为什么需要 constexpr,为什么需要非递归解决方案?
  • @Barmar 我需要模板元功能。 @n.m。好吧,从根本上说,我只是想知道这种技术。需要constexpr 才能将其用作元函数。

标签: visual-studio-2015 c++14 template-meta-programming constexpr pow


【解决方案1】:

所以,如果我理解正确,您编译 C++14 但您的编译器并不完全符合 C++14 constexpr 函数。所以你不能在 constexpr 函数中创建循环。

嗯...我没有你的编译器,所以我不知道你的编译器到底不支持什么,所以我提出了一个基于非递归 constexpr 可变参数的 C++14 解决方案不使用 for 循环的模板函数。嗯...两个函数:一个用于负幂,一个用于非负幂。

希望 VS2015 支持。

负函数如下

模板

constexpr T negPow10 (std::index_sequence<Is...> const &)
 {
   using unused = std::size_t[];

   T ret { 1 };

   (void)unused { 0U, (ret /= 10, Is)... };

   return ret;
 }

非负数(正或零次幂)几乎相等,但使用ret *= 10 而不是ret /= 10

它们是通过以下方式调用的

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

以下是一个完整的编译示例(但请注意,正如 n.m. 所指出的那样,static_assert() 超过 double 电源不可靠)

#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, "!" );
 }

说实话,这个解决方案在std::make_index_sequence 中是(或可以是)有点递归。

【讨论】:

  • VS 2015 似乎无法编译这些 c++14 结构。
  • @n.m. - 这正是我所担心的:如果它不支持 for 循环,则可以预期 constexpr 支持处于 C++11 级别。
  • 重复乘以或除以 10 将失去更大指数的精度。
猜你喜欢
  • 2019-03-09
  • 1970-01-01
  • 2014-03-01
  • 2020-02-10
  • 1970-01-01
  • 2018-07-28
  • 2015-11-08
  • 2020-04-02
相关资源
最近更新 更多