2012-01-26 12 views
7

Visual C++ può emettere C4738 warning:Perché non c'è un avviso come C4738 per il doppio?

memorizzazione risultato galleggiante 32 bit in memoria, possibile perdita di prestazioni

nei casi in cui un 32 bit float sta per essere memorizzato nella memoria invece essere memorizzati in un registro.

La descrizione inoltre dice che usando double risolve il problema. Non capisco perché quest'ultimo è vero.

Perché la memorizzazione di float in memoria comporta una perdita di prestazioni e la memorizzazione di double no?

risposta

7

L'avvertimento combina due questioni:

  • Galleggianti devono essere conservati in memoria piuttosto che registri, che può ridurre le prestazioni (perché la memoria è molto più lento di registri)
  • Galleggianti sarà arrotondato (perché registri avere sempre 64 o 80 bit, ma in memoria un float ha solo 32 bit).

Uso doppie risolve il secondo numero (almeno parzialmente, 64 bit sono ancora meno precisa di 80 bit), ma non ha alcun impatto sulla possibile perdita di prestazioni. Quale è il motivo per cui il decription avvertimento menziona due rimedi:

Per risolvere il problema segnalato ed evitare l'arrotondamento, compilare con/fp: veloce o uso raddoppia invece di carri allegorici.

Per risolvere il problema segnalato e evitare l'esaurimento dei registri, cambiare l'ordine di calcolo e modificare l'utilizzo del inlining

+0

Perché il risultato 'float' comporta una perdita di prestazioni, ma non' double'? – tenfour

+0

@tenfour: * entrambi * provocheranno una perdita di prestazioni, ma il doppio non verrà arrotondato. –

+2

@tenfour: quando si memorizza un registro a virgola mobile a 64 bit in una posizione di memoria a 32 bit, è necessario arrotondare e l'arrotondamento richiede tempo. Quando si memorizza un registro a virgola mobile a 64 bit in una posizione di memoria a 64 bit, non è necessario eseguire il round, è sufficiente memorizzare. (La maggior parte dei punti flottanti ora è inattiva con i registri SSE, quindi la roba a 80 bit è _usualmente_ irrilevante.) –

3

Mentre io non sono sicuro al 100% della causa, ecco la mia ipotesi.

Quando la compilazione su x86 e SSE2 non è abilitata, il compilatore deve utilizzare lo stack FP x87 per tutti i registri a virgola mobile. Su MSVC, la modalità FP, per impostazione predefinita, imposta l'arrotondamento di precisione a 53 bit. (Penso che non ne sia sicuro al 100%.)

Pertanto, tutte le operazioni eseguite sullo stack FP sono a precisione doppia.

Tuttavia, quando qualcosa viene ridotto a uno float, la precisione deve essere arrotondata a precisione singola. L'unico modo per farlo è archiviarlo in memoria tramite l'istruzione fstp su un operando di memoria a 4 byte e ricaricarlo.


Guardiamo l'esempio sul C4738 warning page si è collegato al:

float func(float f) 
{ 
    return f; 
} 

int main() 
{ 
    extern float f, f1, f2; 
    double d = 0.0; 

    f1 = func(d); 
    f2 = (float) d; 
    f = f1 + f2; // C4738 
    printf_s("%f\n", f); 
} 

Quando si chiama func(), d è probabilmente memorizzato in un registro x87. Tuttavia, la chiamata a func() richiede che la precisione venga ridotta a precisione singola. Questo farà sì che d venga arrotondato/archiviato in memoria. Quindi ricaricato e ri-promosso a doppia precisione sulla linea f = f1 + f2;.

Tuttavia, se si utilizza double per tutto il percorso, il compilatore può mantenere d nel registro, evitando così il sovraccarico di andare ae dalla memoria.


Per quanto riguarda il motivo per cui potrebbe farti esaurire i registri ... Non ne ho idea. È possibile che la semantica del programma risulti avere valori di precisione doppia e precisione singola dello stesso valore, che in questo caso richiedono un registro aggiuntivo.

Problemi correlati