2011-12-24 16 views
32

In C++ 11 è std::sqrt definito come constexpr, ovvero può essere utilizzato legalmente da altre funzioni constexpr o in contesti di compilazione come dimensioni di matrice o argomenti del modello? g ++ sembra consentirlo (usando -std=c++0x), ma non sono sicuro di poterlo considerare autorevole dato che il supporto per C++ 0x/C++ 11 è ancora incompleto. Il fatto che non riesca a trovare nulla su Internet mi rende insicuro.In C++ 11 è sqrt definito come constexpr?

Sembra che questo dovrebbe essere qualcosa che si potrebbe facilmente trovare usando Google, ma ho provato (per 40 minuti ora ...) e non ho trovato nulla. Ho potuto trovare diverse proposte per aggiungere constexpr a varie parti della libreria standard (come ad esempio this one), ma nulla su sqrt o altre funzioni matematiche.

risposta

21

std::sqrt non è definito come constexpr, in base alla sezione 26.8 di N3291: il FDIS C++ 11 (e dubito che lo abbiano aggiunto allo standard finale dopo quello). Si potrebbe scrivere una tale versione, ma la versione standard della libreria non è constexpr.

+2

È esplicitamente indicato? 26.La libreria 8 C non fa riferimento a constexpr. Un'implementazione sarebbe sicuramente conforme se fornisse implementazioni della funzione constexpr oltre a quelle senza. – user2023370

+6

@ user643722: un'implementazione di libreria standard * potrebbe * definire una versione 'constexpr'. Ma non è * obbligatorio * farlo, e quindi non puoi davvero fare affidamento su di esso. –

22

Solo nel caso qualcuno è interessato a una funzione di radice quadrata meta intero, qui è uno che ho scritto mentre una fa:

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, che sembra avere una complessità lineare, cioè Theta (valore), piuttosto che logaritmico, che è del tutto raggiungibile, ad es. nella risposta di @Linoliumz. – einpoklum

13

Qui di seguito è un'implementazione radice quadrata constexpr che usa la ricerca binaria. Funziona correttamente fino a 2^64 con gcc e clang, altre versioni più semplici spesso falliscono per i numeri> 2^32 perché i compilatori limitano la profondità di ricorsione ad es. 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); 
} 

Qui di seguito è una versione più bello (per costanti intere), che richiede C++ 14, è simile a quella presentata nel Baptiste Wicht di blog post. Le funzioni di constexpr C++ 14 possono utilizzare variabili locali e istruzioni 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

Perché "MID" è una macro? – dyp

+0

Perché in C++ 11 le variabili const cost all'interno delle funzioni di constexpr non sono consentite. – Linoliumz

+0

È questo per lo spettacolo, o le persone usano davvero qualcosa che richiede oltre 200 ricorsi da compilare? – Mikhail

3

Se guardiamo al progetto di norma più vicino al C++ 11 N3337 possiamo vedere che sqrt non è contrassegnato constexpr, dalla sezione 26.8c.math:

I contenuti di queste intestazioni sono uguali alle intestazioni della libreria C standard e, rispettivamente, alle seguenti modifiche :

nessuna delle modifiche include l'aggiunta di constexpr a sqrt.

si può vedere dalla questione Is gcc considering builtins of non-constant expression functions to be constant expressions, che gcc segna molte funzioni matematiche come constexpr come estensione. Questa estensione è un non-conforming extension, come ho notato nella mia risposta alla domanda collegata quando gcc implementato questo sembrava che sarebbe un'estensione conforme, ma questo è cambiato e gcc è probabile che questa estensione per essere conforme.

9

Ecco una rapida ed efficiente implementazione di constexpr per i numeri in virgola mobile double. È possibile adattarlo anche a float, se necessario:

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