In .NET, perché System.Math.Round(1.035, 2, MidpointRounding.AwayFromZero)
produce 1,03 invece di 1,04? Sento che la risposta alla mia domanda si trova nella sezione "Nota ai chiamanti" allo http://msdn.microsoft.com/en-us/library/ef48waz8.aspx, ma non sono in grado di comprendere la spiegazione.Perché System.MidpointRounding.AwayFromZero non viene arrotondato in questa istanza?
risposta
tuo sospetto è esattamente a destra. I numeri con porzione frazionaria, se espressi come valori letterali in .NET, sono per impostazione predefinita doubles. Un doppio (come un float) è un'approssimazione di un valore decimale, non un preciso valore decimale. È il valore più vicino che può essere espresso in base-2 (binario). In questo caso, l'approssimazione è sempre così sfumata sul lato piccolo di 1.035. Se si scrive utilizzando un decimale esplicito funziona come ci si aspetta:
Console.WriteLine(Math.Round(1.035m, 2, MidpointRounding.AwayFromZero));
Console.ReadKey();
Per comprendere il motivo per cui doppie e carri lavorano il loro modo di fare, immaginare che rappresenta il numero 1/3 in decimale (o binario, che soffre dalla stesso problema). Non puoi- si traduce in .3333333 ...., il che significa che rappresentarlo con precisione richiederebbe una quantità infinita di memoria.
I computer si aggirano intorno usando le approssimazioni. Spiegherei precisamente come, ma probabilmente mi sbagliavo. È possibile leggere tutto qui però: http://en.wikipedia.org/wiki/IEEE_754-1985
Nota: il valore predefinito è * double. * 1.0 è implicitamente un double, sebbene possa essere esplicitamente impostato con 1d o 1.0d. Il suffisso 'f' è richiesto per i letterali float. –
Risolto, grazie Anthony –
@ChrisShain double è davvero un valore preciso; è solo un preciso valore binario, che può o meno essere un'approssimazione al preciso valore decimale indicato dal letterale. Ad esempio, il valore binario '0.25d' è esattamente uguale al valore decimale 0.25. In più penso che intendessi "immagina di rappresentare il numero 1/3 in decimale" perché 0.33333 ... è la rappresentazione decimale, non binaria. La rappresentazione binaria è più simile a 0.0101010101 ... – phoog
Ad una ipotesi direi che internamente 1.035 non può essere rappresentato in binario come esattamente 1.035 ed è probabilmente (sotto il cofano) 1.0349999999999999, che sarebbe il motivo per cui si arrotonda.
Solo un'ipotesi però.
Credo che l'esempio a cui ti riferisci sia un altro problema; per quanto ho capito stanno dicendo che 0.1 non è memorizzato, in float, esattamente come 0.1, in realtà è leggermente fuori causa di come i float sono memorizzati in binario. Supponiamo che in realtà assomigli più a 0.0999999999999 (o simile), qualcosa di molto, molto poco meno di 0,1 - quindi leggermente che non tende a fare molta differenza. Beh, no, stanno dicendo: una differenza evidente sarebbe che aggiungendo questo al tuo numero e l'arrotondamento sembrerebbe effettivamente andare nel modo sbagliato perché anche se i numeri sono estremamente vicini è ancora considerato "inferiore" al valore .5 per arrotondare .
Se ho capito male quella pagina, spero che qualcuno mi corregge :)
non vedo come si riferisce alla vostra chiamata, però, perché sei stato più esplicito. Forse memorizza il tuo numero in modo simile.
Esattamente. –
+1 per "a causa di come i float sono memorizzati in binario". – phoog
Ho regole perché la rappresentazione binaria di 1.035 più vicino a 1.03 che 1.04
Per ottenere risultati migliori farlo in questo modo -
decimal result = decimal.Round(1.035m, 2, MidpointRounding.AwayFromZero);
La rappresentazione binaria di 1.035d è 0x3FF08F5C28F5C28F, che in realtà è 1.03499999999999992006394222699E0, così System.Math.Round (1.035, 2, MidpointRounding.AwayFromZero) resa 1,03 invece di 1,04 , quindi è corretto.
Tuttavia, la rappresentazione binaria di 4.005d è 0x4010051EB851EB85, che è 4,00499999999999989341858963598, così System.Math.Round (4.005, 2, MidpointRounding.AwayFromZero) dovrebbe dare 4,00, ma resa 4.01 che è errato (o una smart ' correggere '). Se lo selezioni in MS SQL seleziona ROUND (CAST (4.005 AS float), 2), è 4.00 Non capisco perché .NET applichi questa 'correzione intelligente' che peggiora le cose.
È possibile controllare la rappresentazione binaria di un doppio a: http://www.binaryconvert.com/convert_double.html
- 1. gradiente non viene arrotondato in IE9
- 2. Perché questa eccezione non viene lanciata?
- 3. Perché questa eccezione non viene rilevata?
- 4. Come viene arrotondato il risultato di questa operazione a precisione singola? [O perché questo bit 1 e non 0?]
- 5. Perché DateTime.AddDays viene arrotondato al millisecondo più prossimo?
- 6. Perché questa matrice di assegnazione della struttura non viene compilata?
- 7. Perché questa attività non finisce?
- 8. Perché questa istanza di codice funziona e non causa un errore di compilazione?
- 9. Perché non funziona questa comprensione?
- 10. Perché questa dichiarazione non funziona
- 11. Perché non posso entrare in questa linea?
- 12. Perché questa divisione non funziona in Python?
- 13. Perché questa multibinding non funziona
- 14. Perché questa CATransition non funziona?
- 15. Perché la mia istanza AWS EC2 termina quando viene interrotta?
- 16. php/phpDoc - istanza @return di $ questa classe?
- 17. Perché questa rotazione dell'elemento non funziona?
- 18. Perché mi viene questa strana interruzione di riga ""?
- 19. Cosa significa questa espressione e perché viene compilata?
- 20. Perché non viene fornito alcun avviso per questa variabile non utilizzata?
- 21. Perché non viene finalmente eseguito?
- 22. Perché viene compilata questa dichiarazione della classe C#?
- 23. Perché questa cache non viene interrogata in ColdFusion 9.01 utilizzando cfscript?
- 24. Perché questa ricorsione NON è infinita?
- 25. Perché non viene chiamato onBackPressed()?
- 26. Perché PostConstruct non viene chiamato?
- 27. Anteprima arrotondato in iphone
- 28. Perché questa subquery NON causa un errore?
- 29. Pulsante arrotondato in Android
- 30. Cosa c'è di sbagliato in questa istanza: ArrowApply Automaton?
È interessante notare che, chiamata a questo metodo con un valore di 1.135 rendimenti 1.14. –
La "nota" è essenzialmente un numero di base 2. Non può rappresentare determinati valori * precisamente. * Digiti 1.035, la rappresentazione interna potrebbe essere 1.034999999982 o qualsiasi altra cosa. Se ti interessa la rappresentazione esatta delle cifre con un determinato numero di cifre decimali, forse System.Decimal è il tipo che fa per te. Soprattutto se si tratta di valori finanziari. –
@KyleTrauberman è perché con 1.135 si ottiene la fortuna - l'approssimazione è leggermente superiore a 1.135, anziché inferiore come nell'OP. –