2011-12-24 13 views
32

En C++ 11 es std::sqrt definido como constexpr, es decir, ¿puede usarse legalmente desde otras funciones constexpr o en contextos de tiempo de compilación como tamaños de matriz o argumentos de plantilla? g ++ parece permitirlo (usando -std=c++0x), pero no estoy seguro de poder tomar eso como autoritario dado que la compatibilidad con C++ 0x/C++ 11 aún está incompleta. El hecho de que parece que no puedo encontrar nada en Internet me hace dudar.En C++ 11 ¿está definido sqrt como constexpr?

Parece que esto debería ser algo que uno podría descubrir fácilmente usando Google, pero lo he intentado (durante 40 minutos ...) y no pude encontrar nada. Pude encontrar varias propuestas para agregar constexpr a varias partes de la biblioteca estándar (como por ejemplo this one), pero nada sobre sqrt u otras funciones matemáticas.

Respuesta

21

std::sqrt no se define como constexpr, de acuerdo con la sección 26.8 de N3291: el C++ 11 FDIS (y I duda añadieron a la norma final después de eso). Uno podría escribir tal versión, pero la versión estándar de la biblioteca no es constexpr.

+2

Es éste declaró que no explícitamente? 26.8 C library no hace referencia a constexpr. Una implementación seguramente todavía estaría de acuerdo si proporcionara implementaciones de función constexpr además de aquellas ordenadas sin. – user2023370

+6

@ user643722: una implementación de biblioteca estándar * podría * definir una versión 'constexpr'. Pero no es * obligatorio * hacerlo, y por lo tanto no se puede confiar realmente en eso. –

22

Por si acaso alguien está interesado en una función de raíz cuadrada de enteros meta, aquí es uno que escribí hace un tiempo:

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

Umm, que parece tener complejidad lineal, es decir, Theta (valor), en lugar de logarítmica, que es bastante alcanzable, p. en la respuesta de @Linoliumz. – einpoklum

13

A continuación se muestra una implementación de raíz cuadrada constexpr que utiliza búsqueda binaria. Funciona correctamente hasta 2^64 con gcc y clang, otras versiones más simples a menudo fallan para números> 2^32 porque los compiladores limitan la profundidad de recursión a, p. 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); 
} 

continuación es una versión más agradable (por constantes enteras) que requiere C++ 14, es similar a la presentada en Baptiste Wicht de blog post. Las funciones constexpr de C++ 14 pueden usar variables locales y sentencias 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); 
} 
+0

¿Por qué 'MID 'es una macro? – dyp

+0

Porque en C++ 11 variables estáticas estáticas dentro de las funciones constexpr no están permitidas. – Linoliumz

+0

¿Es esto para mostrar, o la gente realmente usa algo que toma más de 200 recursiones para compilar? – Mikhail

3

Si nos fijamos en el proyecto de norma más cercano a C++ 11 N3337 podemos ver que sqrt no está marcado constexpr, de la sección 26.8c.math:

El contenido de estos encabezados son los mismos que los de la biblioteca estándar C y respectivamente, con los siguientes cambios :

ninguno de los cambios incluye agregar constexpr a sqrt.

podemos ver en la cuestión Is gcc considering builtins of non-constant expression functions to be constant expressions, que gcc marcas de muchas funciones matemáticas como constexpr como una extensión. Esta extensión es non-conforming extension, como lo noté en mi respuesta a la pregunta vinculada cuando gcc implementó esto, parecía que sería una extensión conforme, pero esto cambió y es probable que gcc corrija esta extensión para que sea conforme.

9

Aquí hay una implementación de constexpr rápida y eficiente para números de coma flotante double. Es posible adaptarlo a float también, si es necesario:

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