2016-05-05 9 views
9

Sto leggendo attraverso il new C++ FAQ e vedo che anche se x == y per double x, y;, allora è possibile che:Il cast e il compito eliminano davvero ogni precisione extra dei float?

std::cos(x) == std::cos(y) 

per valutare a false. Questo perché la macchina può avere un processore che supporta la precisione estesa, in modo che una parte di == sia un numero a 64 bit mentre l'altra sia un numero a 80 bit.

Tuttavia, l'esempio successivo sembra essere corretto:

void foo(double x, double y) 
{ 
    double cos_x = cos(x); 
    double cos_y = cos(y); 
    // the behavior might depend on what's in here 
    if (cos_x != cos_y) { 
    std::cout << "Huh?!?\n"; // You might end up here when x == y!! 
    } 
} 

quanto ne read on en.cppreference.com here:

Fusioni e assegnazione striscia via qualsiasi gamma estraneo e precisione: questi modelli l'azione memorizzando un valore da un registro FPU a precisione estesa in una posizione di memoria di dimensioni standard.

Quindi, assegnando:

double cos_x = cos(x); 
double cos_y = cos(y); 

dovrebbe tagliare l'eventuale precisione in più e rendere il programma perfettamente prevedibile.

Quindi, chi ha ragione? Le domande frequenti su C++ o en.cppreference.com?

+3

Nel tuo esempio, certo, la precisione extra del risultato di 'cos (v)' può essere rimossa. Ciò non significa che la precisione extra di 'v' in' cos (v) 'non cambi quel risultato. Generalmente, le operazioni in virgola mobile sono tutte specifiche della piattaforma, quindi spetta al compilatore avere i flag e la documentazione di quale sarà il comportamento in virgola mobile. Ad esempio, molti compilatori ti permetteranno di dire "Voglio una rigida semantica a virgola mobile a 32 bit", quindi nessuna di queste cose si applica. – GManNickG

+1

Elaborazione sul commento @GManNickG, per esempio GCC ha le opzioni '-ffloat-store' e' -fexcess-precision = 'per scegliere il comportamento desiderato. – rodrigo

+2

BTW: Trovo il tag di lingua-avvocato strano qui. Lo standard C++ non è realmente il documento di riferimento per il punto mobile, e dubito che a tutti * piaccia * passare attraverso le specifiche IEEE/Intel. Di nuovo, si riduce fondamentalmente alla sua "implementazione/hardware". Ma generalmente la premessa della domanda si basa su due diverse fonti non normative, quindi che cosa dovremmo essere il giuramento qui? –

risposta

3

Né le FAQ ISOCPP né il cppreference sono fonti autorevoli. cppreference è essenzialmente l'equivalente di wikipedia: contiene buone informazioni, ma chiunque può aggiungere qualsiasi cosa senza le fonti, è necessario leggerlo con un pizzico di sale. Le seguenti tre affermazioni sono vere:

Hai capito? La propria installazione particolare potrebbe memorizzare il risultato di una delle chiamate cos() nella RAM, troncarla nel processo, quindi confrontare il valore troncato con il risultato non troncato della seconda chiamata cos(). A seconda di molti dettagli, questi due valori potrebbero non essere uguali.

Questo perché la macchina può disporre di un processore che supporta la precisione estesa, in modo che una parte di == sia un numero a 64 bit mentre l'altra sia un numero a 80 bit.

Lanciare e assegnare la striscia lontano da qualsiasi gamma e precisione estranee: questo modello l'azione di memorizzare un valore da un registro FPU a precisione estesa in una posizione di memoria di dimensioni standard.

Forse. Dipende dalla piattaforma, dal compilatore, dalle opzioni del compilatore o da un numero qualsiasi di cose. Realisticamente, le dichiarazioni di cui sopra valgono solo per GCC in modalità a 32 bit, che per impostazione predefinita è la FPU legacy x87 (in cui tutto è internamente a 80 bit) e arrotondato per difetto a 64 bit durante l'archiviazione in memoria. 64 bit utilizza SSE, quindi i doppi temporari sono sempre a 64 bit. È possibile forzare SSE con mfpmath=sse e -msse2. In entrambi i casi, il gruppo potrebbe essere simile a questo:

movsd QWORD PTR [rsp+8], xmm1 
    call cos 
    movsd xmm1, QWORD PTR [rsp+8] 
    movsd QWORD PTR [rsp], xmm0 
    movapd xmm0, xmm1 
    call cos 
    movsd xmm2, QWORD PTR [rsp] 
    ucomisd xmm2, xmm0 

Come potete vedere non v'è alcun troncamento di eventuali registri di ordinamento né 80-bit usati qui.

+0

Grazie per la spiegazione! Ho rimosso il tag dell'avvocato linguistico. Tuttavia, sarei davvero interessato a ciò che lo standard C++ ha da dire sul problema. So che non specifica molto sull'aritmetica in virgola mobile, ma specifica alcune cose importanti. –

1

Vorrei che quell'assegnazione o il lancio gettassero via la precisione in più. Ho lavorato con compilatori "intelligenti" per i quali ciò non accade. Molti lo fanno, ma se vuoi un codice veramente indipendente dalla macchina, devi fare del lavoro extra.

È possibile forzare il compilatore a utilizzare sempre il valore in memoria con la precisione prevista dichiarando la variabile con la parola chiave "volatile".

Alcuni compilatori passano i parametri in registri anziché in pila. Per questi compilatori, potrebbe essere che x o y potrebbe avere una precisione extra involontaria. Per essere completamente sicuro, si potrebbe fare

volatile double db_x = x,   db_y = y; 
volatile double cos_x = cos(db_x), cos_y = cos(db_y); 
if (cos_x != cos_y)... 
Problemi correlati