2015-08-25 14 views
16

Devo verificare una disuguaglianza contenente le radici quadrate. Per evitare risultati errati dovuti alla virgola mobile imprecisioni e arrotondamento, io uso std::nextafter() per ottenere una superiore/inferiore bound:C++ sqrt precisione garantita, limite superiore/inferiore

#include <cfloat> // DBL_MAX 
#include <cmath> // std::nextafter, std::sqrt 

double x = 42.0; //just an example number 
double y = std::nextafter(std::sqrt(x), DBL_MAX); 

a) è y*y >= x garantiti utilizzando compilatore GCC?

b) Funzionerà per altre operazioni come + - * / o std::cos() e std::acos()?

c) Ci sono modi migliori per ottenere limiti superiori/inferiori?

Aggiornamento: I read questo non è garantito dallo standard C++, ma dovrebbe funzionare secondo IEEE-754. Funzionerà con il compilatore GCC?

+1

Stai chiedendo questo caso specifico, o in generale per qualsiasi numero in virgola mobile? NaN rompe qualsiasi tipo di logica immediatamente. –

+3

Per quanto posso dire lo standard non fornisce alcuna garanzia sull'implementazione '' std :: sqrt'', quindi penso che la risposta a questo dipenderà dall'implementazione. –

+0

@ MarkB Intendo qualsiasi numero (positivo) in virgola mobile, 42 è solo un esempio. – Lux

risposta

2

Per GCC this la pagina suggerisce che funzionerà se si utilizza la funzione sqrt incorporata GCC __builtin_sqrt.

Inoltre questo comportamento sarà dipende da come si compila il codice e la macchina che viene eseguito su

  1. Se il processore supporta SSE2 allora si dovrebbe compilare il codice con le bandiere -mfpmath=sse -msse2 per garantire che tutte le operazioni in virgola mobile vengono eseguite utilizzando i registri SSE.

  2. Se il processore non supporta SSE2, allora si dovrebbe utilizzare il tipo di long double per i valori in virgola mobile e compilare con la bandiera -ffloat-store per imporre GCC non utilizzare i registri per memorizzare i valori in virgola mobile (avrete una performance penale per fare questo)

3

in generale, operazioni in virgola mobile si tradurrà in qualche errore ULP. IEEE 754 richiede che i risultati per la maggior parte delle operazioni siano corretti entro 0,5 ULP, ma gli errori possono accumularsi, il che significa che un risultato potrebbe non essere compreso in un ULP del risultato esatto. Ci sono anche dei limiti alla precisione, quindi a seconda del numero di cifre che ci sono nei valori risultanti, potresti anche non lavorare con valori della stessa magnitudine. Anche le funzioni trascendentali sono un po 'notorious per introdurre errori nei calcoli.

Tuttavia, se si sta utilizzando GNU glibc, sqrt sarà corretto entro 0,5 ULP (arrotondato), in modo da essere esempio specifico avrebbe funzionato (trascurando NaN, +/-0, +/-Inf). Sebbene sia probabilmente meglio definire un epsilon come tolleranza di errore e utilizzarlo come limite. Per exmaple,

bool gt(double a, double b, double eps) { 

    return (a > b - eps); 
} 

A seconda del livello di precisione è necessario nei calcoli, si può anche voler usare long double invece.

Quindi, per rispondere alle vostre domande ...

a) è y * y> = x garantita usando il compilatore GCC?

Supponendo di utilizzare GNU glibc o SSE2 intrinseci, sì.

b) Funzionerà per altre operazioni come + - */o anche std :: cos() e std :: acos()?

Supponendo di utilizzare GNU glibc e una operazione, sì. Sebbene alcuni trascendentali non siano garantiti correttamente arrotondati.

c) Ci sono modi migliori per ottenere limiti superiori/inferiori?

È necessario sapere qual è la tolleranza di errore nei calcoli e utilizzarlo come epsilon (che può essere maggiore di un ULP).

0

Per quanto riguarda

c) Ci sono modi migliori per ottenere un limite superiore/inferiore?

Un altro modo è quello di utilizzare un diverso rounding mode, cioè FE_UPWARD o FE_DOWNWARD posto di predefinito FE_TONEAREST. Vedi https://stackoverflow.com/a/6867722 Questo può essere slower, ma è un migliore limite superiore/inferiore.