In C++11 is sqrt defined as constexpr?

旧时模样 提交于 2019-12-04 23:37:41

std::sqrt is not defined as constexpr, according to section 26.8 of N3291: the C++11 FDIS (and I doubt they added it to the final standard after that). One could possibly write such a version, but the standard library version is not constexpr.

Just in case anyone is interested in a meta integer square root function, here is one I wrote while a ago:

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

Here is a fast and efficient constexpr implementation for double floating point numbers. You may adapt it to float too, if needed:

#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();
}

Below is a constexpr square root implementation that uses binary search. It works correctly up to 2^64 with gcc and clang, other simpler versions often fail for numbers > 2^32 because compilers limit the recursion depth to e.g. 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);
}

Below is a nicer version (for integer constants) which requires C++14, it is similar to the one presented in Baptiste Wicht's blog post. C++14 constexpr functions are allowed to use local variables and if statements.

// 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);
}
Shafik Yaghmour

If we look at the closest draft standard to C++11 N3337 we can see that sqrt is not marked constexpr, from section 26.8 c.math:

The contents of these headers are the same as the Standard C library headers and respectively, with the following changes:

none of the changes include adding constexpr to sqrt.

We can see from the question Is gcc considering builtins of non-constant expression functions to be constant expressions, that gcc marks many math functions as constexpr as an extension. This extension is a non-conforming extension, as I note in my answer to the linked question when gcc implemented this it looked like it would be a conforming extension but this changed and gcc is likely to fix this this extension to be conforming.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!