2012-07-31 21 views
8

Sono di fronte problema durante arrotondando il valore decimale in C# utilizzando Math.Round(a, 2);arrotondamento valore decimale

Quando sto arrotondamento 1.275 di 2 punti decimali, il risultato è 1.27. Quando sto facendo lo stesso per 1.375, il risultato è 1.38.

Perché non arrotondare da 1,275 a 1,28?

Grazie

+2

Mi dispiace, non ho trovato lo screenshot ;-) ma ho trovato questo blog: http://weblogs.asp.net/sfurman/archive/2003/03/07/3537.aspx –

+4

Stai effettivamente usando un valore 'decimale', o stai usando' double'? – CodesInChaos

+4

È un po 'di confusione quando dici di arrotondare il valore "decimale"; 'Math.Round (1.275m, 2) = 1.28', mentre se si utilizza il sovraccarico' double' si ottiene, come si scrive, 'Math.Round (1.275, 2) = 1.27'. Il problema è molto probabilmente a causa di inaccuratezza nella rappresentazione in virgola mobile. –

risposta

8

non riesco a riprodurre il problema:

Math.Round(1.275m, 2) => 1.28m 
Math.Round(1.375m, 2) => 1.38m 

ho il sospetto che il vostro reclamo che si utilizza un valore decimal è falso, e che si utilizza double valore invece. double non può rappresentare esattamente molti valori decimali, quindi quando scrivi 1.275, in realtà è 1.27499 ... 1.375 è una delle poche rappresentazioni, quindi è in realtà 1.375.

Se il codice si preoccupa esatta rappresentazione decimale, ad esempio quando si lavora sul denaro, si must uso decimal e non binaria in virgola mobile come double o float.


Ma anche se si utilizza rappresentazione decimale, arrotondamento comporta inaspettatamente per molti utenti:

Math.Round(1.265m, 2) => 1.26m 
Math.Round(1.275m, 2) => 1.28m 

Per impostazione predefinita Math.Round usa MidpointRounding.ToEven, noto anche come Banker's round. Ciò evita di accumulare una distorsione da arrotondare sempre a .5.

È possibile utilizzare un sovraccarico di Round che utilizza una modalità di arrotondamento e impostarlo su AwayFromZero per ottenere il comportamento previsto.

Math.Round(1.275m, 2, MidpointRounding.AwayFromZero) => 1.28m 
+3

Questa sarebbe la spiegazione se 1.275 arrotondato a 1.28, ma non è così. – Guffa

+0

'1.275m' non è uguale a' 1.275'. L'OP ha chiesto "1.275". – ken2k

+0

Il "problema" è riproducibile, basta non inserirlo in decimale – Icarus

2

Questo è un terribile hack, ma provare a utilizzare Format. Inesplicabilmente utilizza l'arrotondamento a cui siamo abituati.

Val(Format(2.25, "0.0")) returns 2.3 

(OR)

Solo per informazioni: Da Net versione 2.0 la sua possibile definire il modo in cui "0,5 casi" sono arrotondati con un parametro MidpointRounding. Può essere ToEven o AwayFromZero. Quindi l'arrotondamento "standard" sarebbe il seguente:

Math.Round(2.25, 1, MidpointRounding.AwayFromZero); 

Ciò restituirebbe il valore "2.3".

7

MSDN ha questo da dire su questo comportamento:

Notes a chiamanti

causa della perdita di precisione che può derivare da rappresentano valori decimali come numeri in virgola mobile o eseguendo operazioni aritmetiche su valori a virgola mobile, in alcuni casi il metodo Round (Double, Int32) potrebbe non apparire per arrotondare i valori dei punti medi a il valore pari più vicino nelle cifre decimali. zione. Questo è illustrato nell'esempio seguente, dove 2.135 è arrotondato a 2.13 anziché 2.14. Ciò si verifica perché internamente il metodo moltiplica il valore per 10 cifre e l'operazione di moltiplicazione in questo caso soffre di una perdita di precisione.

public class Example 
{ 
    public static void Main() 
    { 
     double[] values = { 2.125, 2.135, 2.145, 3.125, 3.135, 3.145 }; 
     foreach (double value in values) 
     Console.WriteLine("{0} --> {1}", value, Math.Round(value, 2)); 

    } 
} 
// The example displays the following output: 
//  2.125 --> 2.12 
//  2.135 --> 2.13 
//  2.145 --> 2.14 
//  3.125 --> 3.12 
//  3.135 --> 3.14 
//  3.145 --> 3.14 
+1

Se il metodo restituisce un risultato errato perché internamente fa qualcosa di impreciso, l'implementazione non è corretta. Più probabilmente, la documentazione MSDN è errata e Math.Round restituisce un risultato imprevisto perché il chiamante non si rendeva conto che il valore che stavano passando non era quello che pensavano. Ad esempio, nella domanda posta qui, il chiamante non stava passando 1.275 ma stava passando un valore leggermente inferiore a 1.275. L'errore si è verificato ** prima ** chiamando Math.Round, non ** in ** it. –

4

Se si dispone di un valore Decimal, lo farà in modo corretto rotonda 1.275 a 1.28.

Se si dispone di un valore Double, non si comporta allo stesso modo, poiché il valore 1.275 non può essere rappresentato esattamente. Se si utilizza il valore double1.275, sarà effettivamente leggermente inferiore al valore esatto 1.275, ad esempio 1.2749999999999999.

Quando arrotondamento tale valore, non sarà exacly tra 1.27 e 1.28 ma leggermente più vicino 1.27, quindi sarà arrotondato anziché verso l'alto.

1

E 'perché il vostro arrotondamento un doppio, e non un decimale:

Console.WriteLine(Math.Round(1.275M, 2)); // outputs 1.28 
Console.WriteLine(Math.Round(1.375M, 2)); // outputs 1.38 

decimale e doppie sono molto diversi

+1

Che cos'è il downvote e nessun commento? –

0

comportamento previsto come da documentazione.

Ad esempio, se decimali è uguale a 1, 2,15 e 2,15 mila sono entrambi arrotondato a 2,2 perché .05 e 0,05 mila sono entrambi a metà strada tra .1 e .2 , e .1 è dispari. Allo stesso modo, se i decimali sono uguali a 1, 2.05 e , 2.05000 sono entrambi arrotondati a 2.0 perché .05 e .05000 sono entrambi a metà tra .0 e .1, e .0 è pari. Il comportamento di questo metodo segue lo standard IEEE 754, sezione 4. Questo tipo di arrotondamento è talvolta chiamato arrotondamento al più vicino o arrotondamento del banchiere. Lo strumento riduce al minimo gli errori di arrotondamento che risultano dall'arrotondamento costante di un valore di punto medio in un'unica direzione. Per controllare il tipo di arrotondamento utilizzato dal metodo Round (Decimal, Int32), chiamare il sovraccarico Decimal.Round (Decimal, Int32, MidpointRounding).

Da Math.Round(decimal, int). Prova

Decimal.Round(a, 2, MidpointRounding.AwayFromZero); 
Problemi correlati