2010-10-25 10 views
10

Sto utilizzando .NET 2.0 con PlatformTarget x64 e x86. Sto dando a Math.Exp lo stesso numero di input e restituisce risultati diversi su entrambe le piattaforme.Perché Math.Exp fornisce risultati diversi tra 32-bit e 64-bit, con lo stesso input, stesso hardware

MSDN dice che non si può contare su un letterale/Doppia analizzato per rappresentare lo stesso numero tra le piattaforme, ma penso che il mio uso di Int64BitsToDouble sotto evita questo problema e garantisce lo stesso ingresso per math.exp su entrambe le piattaforme.

La mia domanda è perché i risultati sono diversi? Avrei pensato che:

  • l'ingresso viene memorizzato nello stesso modo (doppia/64-bit di precisione)
  • dell'FPU farebbe gli stessi calcoli indipendentemente bitness processore
  • l'uscita viene memorizzata in allo stesso modo

So che non dovrei confrontare i numeri in virgola mobile dopo la cifra 15/17 in generale, ma sono confuso sull'incoerenza qui con quella che sembra la stessa operazione sullo stesso hardware.

Qualcuno sa cosa sta succedendo sotto il cofano?

double d = BitConverter.Int64BitsToDouble(-4648784593573222648L); // same as Double.Parse("-0.0068846153846153849") but with no concern about losing digits in conversion 
Debug.Assert(d.ToString("G17") == "-0.0068846153846153849" 
    && BitConverter.DoubleToInt64Bits(d) == -4648784593573222648L); // true on both 32 & 64 bit 

double exp = Math.Exp(d); 

Console.WriteLine("{0:G17} = {1}", exp, BitConverter.DoubleToInt64Bits(exp)); 
// 64-bit: 0.99313902928727449 = 4607120620669726947 
// 32-bit: 0.9931390292872746 = 4607120620669726948 

I risultati sono coerenti su entrambe le piattaforme con JIT attivato o disattivato.

[Edit]

io non sono del tutto soddisfatto delle risposte di seguito ecco alcuni dettagli della mia ricerca.

http://www.manicai.net/comp/debugging/fpudiff/ dice che:

Così 32 bit utilizza registri FPU 80 bit, 64 bit utilizza 128 bit SSE registri.

E la CLI standard dice che doppie possono essere rappresentate con maggiore precisione se l'hardware lo supporta:

[Razionale: Questo design permette la CLI di scegliere una rappresentazione ad alte prestazioni specifico per la piattaforma per numeri a virgola mobile fino a quando non vengono collocati in posizioni di memoria. Ad esempio, potrebbe essere in grado di lasciare le variabili in virgola mobile in registri hardware che forniscono maggiore precisione di quanto richiesto da un utente. Allo Partition I 69 allo stesso tempo, i generatori CIL possono forzare le operazioni a rispettare le regole specifiche della lingua per le rappresentazioni tramite l'uso delle istruzioni di conversione. fine logica]

http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf (12.1.3 Gestione dei tipi di dati a virgola mobile)

credo che questo è quello che sta succedendo qui, perché i risultati diversi dopo di doppie standard di 15 cifre di precisione. Il risultato Math.Exp a 64 bit è più preciso (ha una cifra in più) perché internamente a 64 bit .NET utilizza un registro FPU con maggiore precisione rispetto al registro FPU utilizzato da .NET a 32 bit.

+0

+1 Interessante. Vedo gli stessi identici sintomi sulla mia macchina e il passaggio da x86 a anycpu cambia l'output. – sisve

+1

Il tuo paragrafo finale non è corretto. La versione a 32 bit sarà ** più corretta ** perché utilizza la FPU di precisione estesa x87 a 80 bit, mentre la versione a 64 bit utilizzerà la SSE2 più veloce e più coerente. –

+1

Possibile duplicato di [Differenza in aritmetica in virgola mobile tra x86 e x64] (http://stackoverflow.com/questions/22710272/difference-in-floating-point-arithmetics-between-x86-and-x64) –

risposta

3

Sì errori di arrotondamento, ed in effetti NON è lo stesso hardware. La versione a 32 bit ha come target un diverso set di istruzioni e dimensioni di registro.

+1

È interessante, stai dicendo che esiste un set diverso di istruzioni della FPU? Devo ammettere che non so come sia implementato Math.Exp, sia che si tratti di un'istruzione FPU o di molte. E avrei pensato che i registri della FPU fossero gli stessi su entrambe le piattaforme perché sto usando il tipo "doppio". – Yoshi

+0

Non conosco il minutae dell'implementazione .NET o della x64 fpu, ma non mi sarei mai aspettato che fossero identici. Stai anche convertendo da int a doppio che sta introducendo un errore. – winwaed

+1

Segnalo come risposta perché penso che fornisca il maggior numero di dettagli. Ho trovato più informazioni su questo URL, il che spiega che .NET a 32 bit utilizza registri FPU a 80 bit e .NET a 64 bit utilizza registri SSE a 128 bit: http://www.manicai.net/comp/ debugging/fpudiff/ – Yoshi

3

Con il tipo Double si ottengono errori di arrotondamento, poiché le frazioni in binario diventano molto grandi molto rapidamente. Potrebbe essere utile se hai usato il tipo Decimale.

+0

I (penso) Lo capisco, ma gli errori di arrotondamento che si verificano sullo stesso calcolo sullo stesso input sullo stesso hardware dovrebbero al minimo essere coerente, giusto? O non vi è alcuna garanzia di ciò a causa di altri fattori? – Yoshi

Problemi correlati