###

Question:

I see that, in C#, rounding a `decimal`

, by default, uses `MidpointRounding.ToEven`

. This is expected, and is what the C# spec dictates. However, given the following:

- A
`decimal dVal`

- A format
`string sFmt`

that, when passed in to`dVal.ToString(sFmt)`

, will result in a string containing a rounded version of`dVal`

...it is apparent that `decimal.ToString(string)`

returns a value rounded using `MidpointRounding.AwayFromZero`

. This would appear to be a direct contradiction of the C# spec.

**My question is this:** is there a good reason this is the case? Or is this just an inconsistency in the language?

Below, for reference, I've included some code that writes to console an assortment of rounding operation results and `decimal.ToString(string)`

operation results, each on every value in an array of `decimal`

values. The actual outputs are embedded. After that, I've included a relevant paragraph from the C# Language Specification section on the `decimal`

type.

The example code:

`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(); } `

The paragraph from section 4.1.7 of the C# Language Specification ("The decimal type") (get the full spec here (.doc)):

The result of an operation on values of type decimal is that which would result from calculating an exact result (preserving scale, as defined for each operator) and then rounding to fit the representation. Results are rounded to the nearest representable value, and, when a result is equally close to two representable values, to the value that has an even number in the least significant digit position (this is known as â€œbankerâ€™s roundingâ€). A zero result always has a sign of 0 and a scale of 0.

It's easy to see that they may not have been considering `ToString(string)`

in this paragraph, but I'm inclined to think it fits in this description.

###

Solution:1

`ToString()`

by default formats according to the `Culture`

, not according to a computational aspect of the specification. Apparently the `Culture`

for your locale (and most, from the looks of it) expects rounding away from zero.

If you want different behavior, you can pass an `IFormatProvider`

in to `ToString()`

I thought the above, but you are correct that it always rounds away from zero no matter the `Culture`

. Check the links in the comments for proof.

###

Solution:2

If you read the spec carefully, you will see that there is no inconsistency here.

Here's that paragraph again, with the important parts highlighted:

The result of an

operation on values of type decimalis that which would result from calculating an exact result (preserving scale, as defined for each operator) and then rounding to fit the representation. Results are rounded to thenearest representable value, and, when a result is equally close to two representable values, to the value that has an even number in the least significant digit position (this is known as â€œbankerâ€™s roundingâ€). A zero result always has a sign of 0 and a scale of 0.

This part of the spec applies to **arithmetic operations** on `decimal`

; string formatting is not one of those, and even if it were, it wouldn't matter because your examples are low-precision.

To demonstrate the behaviour referred to in the spec, use the following code:

`Decimal d1 = 0.00000000000000000000000000090m; Decimal d2 = 0.00000000000000000000000000110m; // Prints: 0.0000000000000000000000000004 (rounds down) Console.WriteLine(d1 / 2); // Prints: 0.0000000000000000000000000006 (rounds up) Console.WriteLine(d2 / 2); `

That's all the spec is talking about. If the result of some calculation would exceed the precision limit of the `decimal`

type (29 digits), banker's rounding is used to determine what the result will be.

###

Solution:3

Most likely because this is the standard way of dealing with currency. The impetus for the creation of decimal was that floating point does a poor job of dealing with currency values, so you would expect it's rules to be more aligned with accounting standards than mathematical correctness.

**Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com**

EmoticonEmoticon