2012-02-10 9 views
23

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?

+0

È interessante notare che, chiamata a questo metodo con un valore di 1.135 rendimenti 1.14. –

+10

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. –

+1

@KyleTrauberman è perché con 1.135 si ottiene la fortuna - l'approssimazione è leggermente superiore a 1.135, anziché inferiore come nell'OP. –

risposta

26

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

+0

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. –

+0

Risolto, grazie Anthony –

+0

@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

1

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ò.

3

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.

+0

Esattamente. –

+0

+1 per "a causa di come i float sono memorizzati in binario". – phoog

6

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); 
7

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

Problemi correlati