Vedo che, in C#, l'arrotondamento a decimal
, per impostazione predefinita, utilizza MidpointRounding.ToEven
. Questo è previsto ed è ciò che detta la specifica C#. Tuttavia, data la seguente:Perché .NET decimal.ToString (stringa) è più o meno lontano da zero, apparentemente incoerente con le specifiche della lingua?
- Un
decimal dVal
- Un formato
string sFmt
che, quando passato adVal.ToString(sFmt)
, si tradurrà in una stringa contenente una versione arrotondata delladVal
... è apparente che decimal.ToString(string)
restituisce un valore arrotondato utilizzando MidpointRounding.AwayFromZero
. Ciò sembrerebbe essere una contraddizione diretta della specifica C#.
La mia domanda è questa: c'è una buona ragione questo è il caso? O è solo un'incongruenza nella lingua?
Di seguito, per riferimento, ho incluso del codice che scrive per consolare un assortimento di risultati dell'operazione di arrotondamento e risultati dell'operazione decimal.ToString(string)
, ciascuno su ogni valore in una matrice di valori decimal
. Le uscite effettive sono incorporate. Dopodiché, ho incluso un paragrafo pertinente dalla sezione C# Language Specification sul tipo decimal
.
Il codice di esempio:
static void Main(string[] args)
{
decimal[] dArr = new decimal[] { 12.345m, 12.355m };
OutputBaseValues(dArr);
// Base values:
// d[0] = 12.345
// d[1] = 12.355
OutputRoundedValues(dArr);
// Rounding with default MidpointRounding:
// Math.Round(12.345, 2) => 12.34
// Math.Round(12.355, 2) => 12.36
// decimal.Round(12.345, 2) => 12.34
// decimal.Round(12.355, 2) => 12.36
OutputRoundedValues(dArr, MidpointRounding.ToEven);
// Rounding with mr = MidpointRounding.ToEven:
// Math.Round(12.345, 2, mr) => 12.34
// Math.Round(12.355, 2, mr) => 12.36
// decimal.Round(12.345, 2, mr) => 12.34
// decimal.Round(12.355, 2, mr) => 12.36
OutputRoundedValues(dArr, MidpointRounding.AwayFromZero);
// Rounding with mr = MidpointRounding.AwayFromZero:
// Math.Round(12.345, 2, mr) => 12.35
// Math.Round(12.355, 2, mr) => 12.36
// decimal.Round(12.345, 2, mr) => 12.35
// decimal.Round(12.355, 2, mr) => 12.36
OutputToStringFormatted(dArr, "N2");
// decimal.ToString("N2"):
// 12.345.ToString("N2") => 12.35
// 12.355.ToString("N2") => 12.36
OutputToStringFormatted(dArr, "F2");
// decimal.ToString("F2"):
// 12.345.ToString("F2") => 12.35
// 12.355.ToString("F2") => 12.36
OutputToStringFormatted(dArr, "###.##");
// decimal.ToString("###.##"):
// 12.345.ToString("###.##") => 12.35
// 12.355.ToString("###.##") => 12.36
Console.ReadKey();
}
private static void OutputBaseValues(decimal[] dArr)
{
Console.WriteLine("Base values:");
for (int i = 0; i < dArr.Length; i++) Console.WriteLine("d[{0}] = {1}", i, dArr[i]);
Console.WriteLine();
}
private static void OutputRoundedValues(decimal[] dArr)
{
Console.WriteLine("Rounding with default MidpointRounding:");
foreach (decimal d in dArr) Console.WriteLine("Math.Round({0}, 2) => {1}", d, Math.Round(d, 2));
foreach (decimal d in dArr) Console.WriteLine("decimal.Round({0}, 2) => {1}", d, decimal.Round(d, 2));
Console.WriteLine();
}
private static void OutputRoundedValues(decimal[] dArr, MidpointRounding mr)
{
Console.WriteLine("Rounding with mr = MidpointRounding.{0}:", mr);
foreach (decimal d in dArr) Console.WriteLine("Math.Round({0}, 2, mr) => {1}", d, Math.Round(d, 2, mr));
foreach (decimal d in dArr) Console.WriteLine("decimal.Round({0}, 2, mr) => {1}", d, decimal.Round(d, 2, mr));
Console.WriteLine();
}
private static void OutputToStringFormatted(decimal[] dArr, string format)
{
Console.WriteLine("decimal.ToString(\"{0}\"):", format);
foreach (decimal d in dArr) Console.WriteLine("{0}.ToString(\"{1}\") => {2}", d, format, d.ToString(format));
Console.WriteLine();
}
Il paragrafo dalla sezione 4.1.7 del linguaggio C# Specification ("Il tipo di decimali") (ottenere il pieno spec here (.doc)):
Il risultato di un'operazione sui valori di tipo decimale è quello che risulterebbe dal calcolo di un risultato esatto (scala di conservazione, come definito per ciascun operatore) e quindi arrotondamento per adattarsi alla rappresentazione. I risultati sono arrotondati al valore rappresentativo più vicino e, quando un risultato è ugualmente vicino a due valori rappresentabili, al valore che ha un numero pari nella posizione di cifra meno significativa (questo è noto come "arrotondamento bancario"). Un risultato pari a zero ha sempre un segno di 0 e una scala di 0.
È facile vedere che in questo paragrafo potrebbero non aver considerato ToString(string)
, ma sono propenso a pensare che si adatti a questa descrizione.
È possibile che si consideri che C# non ha il metodo 'ToString (stringa)'. .NET Framework lo fa. Non sono sicuro che .NET Framework sia vincolato all'osservanza delle regole di un particolare linguaggio di programmazione. –