Ho un problema in cui sembra che i risultati di alcuni calcoli cambino dopo aver utilizzato il Microsoft ACE driver per aprire un foglio di calcolo di Excel.Il driver Microsoft ACE modifica la precisione in virgola mobile nel resto del mio programma
Il codice restituisce il problema.
Le prime due chiamate a DoCalculation
producono gli stessi risultati. Quindi chiamo la funzione OpenSpreadSheet
che apre e chiude un foglio di calcolo Excel 2003 utilizzando il driver ACE. Non ti aspetteresti che OpenSpreadSheet
abbia alcun effetto sull'ultima chiamata a DoCalculation
ma risulta che il risultato effettivamente cambia. Questo è l'output generato dal programma:
1,59142713593566
1,59142713593566
1,59142713593495
Nota le differenze sugli ultimi 3 decimali. Questo non sembra una grande differenza, ma nel nostro codice di produzione i calcoli sono complessi e le differenze risultanti sono piuttosto grandi.
Non fa alcuna differenza se utilizzo il driver JET invece del driver ACE. Se cambio i tipi da doppio a decimale, l'errore scompare. Ma questa non è un'opzione nel nostro codice di produzione.
Sono in esecuzione su Windows 7 64 bit e gli assembly sono compilati per .NET 4.5 x86. L'utilizzo del driver ACE a 64 bit non è un'opzione in quanto viene eseguito Office a 32 bit.
Qualcuno sa perché questo sta accadendo e come posso risolvere il problema?
Il seguente codice riproduce il problema:
static void Main(string[] args)
{
DoCalculation();
DoCalculation();
OpenSpreadSheet();
DoCalculation();
}
static void DoCalculation()
{
// Multiply two randomly chosen number 10.000 times.
var d1 = 1.0003123132;
var d3 = 0.999734234;
double res = 1;
for (int i = 0; i < 10000; i++)
{
res *= d1 * d3;
}
Console.WriteLine(res);
}
public static void OpenSpreadSheet()
{
var cn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;data source=c:\temp\workbook1.xls;Extended Properties=Excel 8.0");
var cmd = new OleDbCommand("SELECT [Column1] FROM [Sheet1$]", cn);
cn.Open();
using (cn)
{
using (OleDbDataReader reader = cmd.ExecuteReader())
{
// Do nothing
}
}
}
Grazie per la tua risposta completa. Mi rendo conto che un risultato non è più corretto dell'altro, ma è un problema che il driver ACE ha questi effetti collaterali che causano cambiamenti nei calcoli fatti all'interno dello stesso processo. Ho provato i tuoi metodi e funzionano entrambi. Il metodo di eccezione funziona solo per le build di debug. Grazie per il tuo tempo. –
Hmm, no, questo reset viene eseguito anche nella versione Release. Si trova all'interno del CLR, codice che non è influenzato dal modo in cui hai creato il tuo programma. Tenete presente l'ultimo paragrafo, otterrete risultati diversi nella versione di rilascio che dipendono dal fatto che sia stato collegato o meno un debugger. –
Oh, mio errore. Volevo dire che il metodo dell'eccezione funziona solo per le build di RELEASE, non per le build di debug. –