In che modo un C # valuta la virgola mobile nel passaggio del mouse sopra e nella finestra intermedia rispetto a quella compilata?

StackOverflow https://stackoverflow.com/questions/1405486

  •  05-07-2019
  •  | 
  •  

Domanda

Sto vedendo qualcosa di strano nel memorizzare doppi in un dizionario e sono confuso sul perché.

Ecco il codice:

            Dictionary<string, double> a = new Dictionary<string, double>();
            a.Add("a", 1e-3);

            if (1.0 < a["a"] * 1e3)
                Console.WriteLine("Wrong");

            if (1.0 < 1e-3 * 1e3)
                Console.WriteLine("Wrong");

La seconda istruzione if funziona come previsto; 1.0 non è inferiore a 1.0. Ora, la prima istruzione if viene valutata come vera. La cosa molto strana è che quando passo con il mouse su if, l'intellisense mi dice falso, ma il codice si sposta felicemente su Console.WriteLine.

Questo è per C # 3.5 in Visual Studio 2008.

È un problema di precisione in virgola mobile? Allora perché la seconda istruzione if funziona? Sento che mi manca qualcosa di fondamentale qui.

Qualsiasi approfondimento è apprezzato.

Modifica2 (rielaborando un po 'la domanda):

Posso accettare il problema della precisione matematica, ma la mia domanda ora è: perché il passaggio del mouse viene valutato correttamente? Questo vale anche per la finestra intermedia. Ho incollato il codice dalla prima istruzione if nella finestra intermedia e viene valutato falso.

Aggiorna

Prima di tutto, grazie mille per tutte le ottime risposte.

Ho anche problemi a ricrearlo in un altro progetto sulla stessa macchina. Osservando le impostazioni del progetto, non vedo differenze. Osservando l'IL tra i progetti, non vedo differenze. Osservando lo smontaggio, non vedo differenze apparenti (oltre agli indirizzi di memoria). Tuttavia, quando eseguo il debug del progetto originale, vedo: screenshot del problema

La finestra immediata mi dice se è falso, ma il codice cade nel condizionale.

Ad ogni modo, penso che la migliore risposta sia prepararsi all'aritmetica in virgola mobile in queste situazioni. Il motivo per cui non ho potuto lasciarlo andare ha più a che fare con i calcoli del debugger diversi dal runtime. Quindi grazie mille a Brian Gideon e al stephentyrone per alcuni commenti molto perspicaci.

È stato utile?

Soluzione

È un problema di precisione mobile.

La seconda istruzione funziona perché il compilatore conta l'espressione 1e-3 * 1e3 prima di emettere il file .exe.

Cerca in ILDasm / Reflector, emetterà qualcosa del genere

 if (1.0 < 1.0)
                Console.WriteLine("Wrong");

Altri suggerimenti

Il problema qui è abbastanza sottile. Il compilatore C # non emette (sempre) il codice che esegue il calcolo in doppio, anche quando si tratta del tipo specificato. In particolare, emette codice che esegue il calcolo in "estesa" precisione usando le istruzioni x87, senza arrotondare i risultati intermedi al doppio.

A seconda che 1e-3 sia valutato come doppio o doppio lungo e che la moltiplicazione sia calcolata in doppio o doppio lungo, è possibile ottenere uno dei seguenti tre risultati:

  • (long double) 1e-3 * 1e3 calcolato in long double è 1.0 - epsilon
  • (doppio) 1e-3 * 1e3 calcolato in doppio è esattamente 1.0
  • (doppio) 1e-3 * 1e3 calcolato in doppio lungo è 1.0 + epsilon

Chiaramente, il primo confronto, quello che non riesce a soddisfare le tue aspettative, viene valutato nel modo descritto nel terzo scenario che ho elencato. 1e-3 viene arrotondato per raddoppiare o perché lo si sta memorizzando e si sta caricando di nuovo, il che forza l'arrotondamento o perché C # riconosce 1e-3 come un letterale a doppia precisione e lo tratta in questo modo. La moltiplicazione viene valutata in doppio lungo perché C # ha un modello numerico cerebrale , in questo modo il compilatore sta generando il codice.

La moltiplicazione nel secondo confronto viene valutata in base a uno degli altri due metodi, (puoi capire quale provando " 1 > 1e-3 * 1e3 "), oppure il compilatore sta arrotondando il risultato di la moltiplicazione prima di confrontarla con 1.0 quando valuta l'espressione in fase di compilazione.

È probabile che tu possa dire al compilatore di non usare la precisione estesa senza che tu lo dica tramite alcune impostazioni di compilazione; anche abilitare codegen su SSE2 può funzionare.

Vedi le risposte qui

Umm ... strano. Non sono in grado di riprodurre il tuo problema. Sto usando anche C # 3.5 e Visual Studio 2008. Ho digitato il tuo esempio esattamente come è stato pubblicato e non vedo l'istruzione Console.WriteLine eseguita.

Inoltre, la seconda istruzione if viene ottimizzata dal compilatore. Quando esamino sia il debug che le build di rilascio in ILDASM / Reflector non vedo alcuna prova di ciò. Questo perché da quando ricevo un avviso del compilatore che dice che è stato rilevato un codice non raggiungibile su di esso.

Infine, non vedo comunque come questo possa essere un problema di precisione in virgola mobile. Perché il compilatore C # dovrebbe valutare staticamente due doppi in modo diverso rispetto al CLR in fase di esecuzione? Se così fosse, allora si potrebbe sostenere che il compilatore C # ha un bug.

Modifica: dopo averci pensato un po 'di più, sono ancora più convinto che questo non sia non un problema di precisione in virgola mobile. È necessario essersi imbattuto in un bug nel compilatore o nel debugger oppure il codice che è stato pubblicato non è esattamente rappresentativo del codice effettivo in esecuzione. Sono molto scettico su un bug nel compilatore, ma un bug nel debugger sembra più probabile. Prova a ricostruire il progetto ed eseguirlo di nuovo. Forse le informazioni di debug compilate insieme a exe non sono state sincronizzate o qualcosa del genere.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top