在C++11中sqrt定义为constexpr吗?

问题描述 投票:0回答:7

在 C++11 中,

std::sqrt
定义为
constexpr
,即可以在其他
constexpr
函数或编译时上下文(如数组大小或模板参数)中合法使用它吗? g++ 似乎允许这样做(使用
-std=c++0x
),但我不确定我是否可以将其视为权威,因为 c++0x/c++11 支持仍然不完整。事实上,我似乎在互联网上找不到任何东西,这让我不确定。

看起来这应该是人们可以使用 Google 轻松找到的东西,但我已经尝试过(现在已经 40 分钟了......)但找不到任何东西。我可以找到几个将 constexpr 添加到标准库各个部分的建议(例如 this one),但没有关于

sqrt
或其他数学函数的内容。

c++ c++11 constexpr
7个回答
35
投票
根据 N3291:C++11 FDIS 第 26.8 节,

std::sqrt
未定义为
constexpr
(我怀疑他们之后将其添加到最终标准中)。人们可能会编写这样一个版本,但标准库版本不是
constexpr


29
投票

这里是

double
浮点数的快速高效的 constexpr 实现。如果需要,您也可以将其改编为
float

#include <limits>   

namespace Detail
{
    double constexpr sqrtNewtonRaphson(double x, double curr, double prev)
    {
        return curr == prev
            ? curr
            : sqrtNewtonRaphson(x, 0.5 * (curr + x / curr), curr);
    }
}

/*
* Constexpr version of the square root
* Return value:
*   - For a finite and non-negative value of "x", returns an approximation for the square root of "x"
*   - Otherwise, returns NaN
*/
double constexpr sqrt(double x)
{
    return x >= 0 && x < std::numeric_limits<double>::infinity()
        ? Detail::sqrtNewtonRaphson(x, x, 0)
        : std::numeric_limits<double>::quiet_NaN();
}

25
投票

以防万一有人对元整数平方根函数感兴趣,这是我之前写的:

constexpr std::size_t isqrt_impl
    (std::size_t sq, std::size_t dlt, std::size_t value){
    return sq <= value ?
        isqrt_impl(sq+dlt, dlt+2, value) : (dlt >> 1) - 1;
}

constexpr std::size_t isqrt(std::size_t value){
    return isqrt_impl(1, 3, value);
}

15
投票

下面是一个使用二分搜索的 constexpr 平方根实现。它可以在 gcc 和 clang 的 2^64 范围内正常工作,其他更简单的版本通常在数字 > 2^32 时失败,因为编译器将递归深度限制为例如 2^64。 200.

// C++11 compile time square root using binary search

#define MID ((lo + hi + 1) / 2)

constexpr uint64_t sqrt_helper(uint64_t x, uint64_t lo, uint64_t hi)
{
  return lo == hi ? lo : ((x / MID < MID)
      ? sqrt_helper(x, lo, MID - 1) : sqrt_helper(x, MID, hi));
}

constexpr uint64_t ct_sqrt(uint64_t x)
{
  return sqrt_helper(x, 0, x / 2 + 1);
}

下面是一个更好的版本(针对整数常量),需要 C++14,它类似于 Baptiste Wicht 的博客文章中介绍的版本。 C++14 constexpr 函数允许使用局部变量和 if 语句。

// C++14 compile time square root using binary search

template <typename T>
constexpr T sqrt_helper(T x, T lo, T hi)
{
  if (lo == hi)
    return lo;

  const T mid = (lo + hi + 1) / 2;

  if (x / mid < mid)
    return sqrt_helper<T>(x, lo, mid - 1);
  else
    return sqrt_helper(x, mid, hi);
}

template <typename T>
constexpr T ct_sqrt(T x)
{
  return sqrt_helper<T>(x, 0, x / 2 + 1);
}

13
投票

现在有一个提案 P1383R0 More constexpr for

<cmath>
<complex>
(Edward J. Rosten, Oliver J. Rosten)
不幸的是还没有进入 C++20 并且从最新的评论来看可能不在C++23 也可以。 C++26?


10
投票

如果我们查看最接近 C++11 N3337 的标准草案,我们可以看到

sqrt
未标记为 constexpr,来自
26.8
c.math 部分:

这些头文件的内容与标准C库相同 标题 和 分别具有以下内容 变化:

所有更改均不包括将 constexpr 添加到

sqrt

我们可以从问题gcc是否将非常量表达式函数的内置函数视为常量表达式看到,

gcc
将许多数学函数标记为constexpr作为扩展。这个扩展是一个不合格的扩展,正如我在回答链接问题时注意到的那样,当
gcc
实现这个时,它看起来像是一个合格的扩展,但是这个改变了,
gcc
很可能修复这个这个扩展保持一致。


0
投票

这是一个 C++23

consteval
版本,其灵感来自 Alex 的递归答案,但取消了递归,以获得更紧凑且编译速度稍快的代码:

#include <cmath>
#include <limits>

consteval double sqrt_consteval(double val) {
  if(std::signbit(val)) return std::numeric_limits<double>::quiet_NaN();
  if(val == std::numeric_limits<double>::infinity()) return std::numeric_limits<double>::quiet_NaN();

  double result{val};
  for(double last{0.0}; result != last; result = 0.5 * (result + val / result)) last = result;
  return result;
}

在 Godbolt 上尝试一下:https://godbolt.org/z/WbjGWcYMr

© www.soinside.com 2019 - 2024. All rights reserved.