2014-05-03 13 views
8

In G ++, varie funzioni matematiche incorporate sono constexpr in determinate condizioni. Ad esempio, le seguenti compilazioni:__builtin_round non è un'espressione costante

static constexpr double A = __builtin_sqrt(16.0); 
static constexpr double B = __builtin_pow(A, 2.0); 

Non sono sempre constexpr, tuttavia, dipende dall'argomento. Ad esempio, __builtin_sqrt(NAN) genera un errore di compilazione quando viene utilizzato come espressione costante.

Ma mi colpisce uno strano caso in cui mi sembra che dovrebbe essere constexpr, ma non è così:

static constexpr double value() { return 1.23; } 
static constexpr double result = __builtin_round(__builtin_sqrt(value())); 

Questo produce:

a.cpp:2:73: error: ‘__builtin_round(1.1090536506409416e+0)’ is not a constant expression 
static constexpr double result = __builtin_round(__builtin_sqrt(value())); 
                     ^

Ho provato varianti del codice sopra, e ho trovato che:

  • Il __builtin_round ha un ruolo speciale nel problema. Sostituendolo con qualche altra funzione matematica incorporata, come ad esempio sqrt o pow, si risolve l'errore. Sembrerebbe quindi che il __builtin_round manchi semplicemente del supporto di constexpr. Ma ...
  • Se value() è sostituito da un valore letterale 1.23, anche questo rimuove l'errore.
  • La rimozione di __builtin_sqrt, lasciando solo __builtin_round(value()), rimuove anche l'errore.

Mi piacerebbe sapere perché il round si comporta in questo modo e se c'è qualche soluzione.

NOTA. Sono consapevole del fatto che le funzioni matematiche incorporate, con la loro consorteprecisione, sono una caratteristica specifica del compilatore non standard. Per favore, non darmi lezioni su come non dovrei usarlo, o su come non dovrei provare a fare matematica matematica in fase di compilazione. Nel mio caso, avere la matematica di constexpr è una caratteristica importante, e sto bene a seconda di G ++.

+0

L'arrotondamento non dipende dal metodo di arrotondamento in virgola mobile corrente, che è noto solo in fase di esecuzione? –

+0

@AlanStokes Ho insegnato anch'io, ma gcc presuppone comunque che la modalità di arrotondamento sia nota al momento della compilazione. Vedi l'opzione '-fno-rounding-math' in gcc. Inoltre, se fosse dovuto a ciò, almeno in teoria praticamente tutti gli operatori FP non sarebbero constexpr (che, almeno in G ++, sono constexpr, simili alla maggior parte delle funzioni matematiche). –

risposta

3

Ho un'idea di un'altra soluzione alternativa.

C'è: utilizzare una funzione di supporto pass_through

template<typename T> 
constexpr T&& pass_through (T&& t) { return static_cast<T&&>(t); } 

usare in questo modo:

static constexpr double value() { return 1.23; } 
static constexpr double result = __builtin_round(pass_through(__builtin_sqrt(value()))); 

Questo codice è stato compilato senza errori nel G ++.

Sono inoltre d'accordo, l'opinione che questo problema dovrebbe essere segnalato al GCC.

2

Posso solo rispondere a una parte della tua domanda, che è se c'è una soluzione alternativa.

Esiste: utilizzare una variabile di supporto constexpr.

Anche se __builtin_round(__builtin_sqrt(value())) viene rifiutato come espressione costante, viene accettato helper = value() e, successivamente, result = __builtin_round(__builtin_sqrt(helper)). In alternativa, helper = __builtin_sqrt(value()) e quindi result = __builtin_round(helper).

Questa incoerenza mostra che GCC è chiaramente in grado di valutare l'espressione in fase di compilazione ed è disposto a considerarlo come constexpr. Anche se potrebbe non trovare supporto nello standard, potrebbe valere la pena segnalarlo come una richiesta di miglioramento sul bug tracker di GCC.

quanto riguarda un motivo reale, che sarebbe indovinare che GCC esegue prima semplice piegatura costante, quindi controlla se constexpr richiede ulteriore piegatura costante, in caso affermativo, controlla se l'espressione corrisponde ai requisiti constexpr, e se così, calcola il risultato. Il controllo fallirebbe, poiché l'espressione non è tecnicamente valida, ma la variabile helper aggiuntiva migliorerebbe la semplice piegatura costante in modo che, nel momento in cui viene verificata la validità, le funzioni incorporate non valide non siano più presenti. Ma come ho detto, è una supposizione.

1

Questo sembra funzionare su g ++ 4.8.2:

static constexpr double value() { return 1.23; } 
static constexpr double root(double x) { return sqrt(x);} 
static constexpr double result = roundl(root(value())); 

È interessante notare che, se sostituisco roundl con round il compilatore si lamenta:

error: ‘round(1.1090536506409416e+0)’ is not a constant expression 

questo funziona anche:

static constexpr double value() { return 1.23; } 
static constexpr double roundroot(double x) { return roundl(sqrt(x));} 
static constexpr double result = roundroot(value()); 

Ma ancora, solo con roundl e non con round. Tutto questo è vero anche quando si usano le corrispondenti versioni __builtin_ (che si limitano a racchiudere).

Sto scaricando la sorgente gcc ora per dare un'occhiata ma non ho ancora una risposta a "perché".

Problemi correlati