Silverlight Midpoint Rounding Solution
As a follow up to my previous Shakespearean-titled post on rounding, I decided to provide a quick follow up to provide a solution for midpoint rounding in Silverlight. Based on some traffic that I’ve been getting it looks like people are searching for a rounding solution in Silverlight since the Silverlight core framework lacks the traditional Math.Round() overloads we get with the full .NET framework that allow specifying the desired midpoint rounding behavior.
Remember, Silverlight contains a pared-down version of the .NET framework.
So, here is a simple solution to take what we learned in the previous post and develop our own MathExt helper class that provides static Round methods similar to the Math.Round() methods that allow us to specify the desired midpoint rounding behavior. Using this you can now control midpoint rounding behavior in Silverlight.
public enum MidpointRounding
{
ToEven,
AwayFromZero
}
public static class MathExt
{
public static decimal Round(decimal d, MidpointRounding mode)
{
return MathExt.Round(d, 0, mode);
}
/// <summary>
/// Rounds using arithmetic (5 rounds up) symmetrical (up is away from zero) rounding
/// </summary>
/// <param name="d">A Decimal number to be rounded.</param>
/// <param name="decimals">The number of significant fractional digits (precision) in the return value.</param>
/// <returns>The number nearest d with precision equal to decimals. If d is halfway between two numbers, then the nearest whole number away from zero is returned.</returns>
public static decimal Round(decimal d, int decimals, MidpointRounding mode)
{
if (mode == MidpointRounding.ToEven)
{
return decimal.Round(d, decimals);
}
else
{
decimal factor = Convert.ToDecimal(Math.Pow(10, decimals));
int sign = Math.Sign(d);
return Decimal.Truncate(d * factor + 0.5m * sign) / factor;
}
}
}
Or if you prefer an extension-method approach:
public enum MidpointRounding
{
ToEven,
AwayFromZero
}
public static class DecimalExtensions
{
public static decimal Round(this decimal d, MidpointRounding mode)
{
return d.Round(0, mode);
}
/// <summary>
/// Rounds using arithmetic (5 rounds up) symmetrical (up is away from zero) rounding
/// </summary>
/// <param name="d">A Decimal number to be rounded.</param>
/// <param name="decimals">The number of significant fractional digits (precision) in the return value.</param>
/// <returns>The number nearest d with precision equal to decimals. If d is halfway between two numbers, then the nearest whole number away from zero is returned.</returns>
public static decimal Round(this decimal d, int decimals, MidpointRounding mode)
{
if (mode == MidpointRounding.ToEven)
{
return decimal.Round(d, decimals);
}
else
{
decimal factor = Convert.ToDecimal(Math.Pow(10, decimals));
int sign = Math.Sign(d);
return Decimal.Truncate(d * factor + 0.5m * sign) / factor;
}
}
}
Remember, the non-extension-method version could easily be used in any legacy .NET projects that still may be on 1.0 or 1.1 to give us the ability to control midpoint rounding behavior which didn’t arrive in the framework until .NET 2.0.
Once again, Happy Rounding!

about 3 years ago
Thank you sir!
about 2 years ago
Good Job !!
However, there is one small bug in your code, for the extension method,
public static decimal Round(this decimal d, MidpointRounding mode)
{
return d.Round(d, 0, mode);
}
–> this line gives compile erroe – return d.Round(d, 0, mode);
It should be
return d.Round(0,mode);
Regards,
Vinay
about 2 years ago
Thanks Vinay!
I’ve updated it to fix the bug.
-Adam
about 2 years ago
Hi there, there is a problem with this approach. It assumes that there is not more than one number on the right side of the rounding accuracy required. For example if I have a number 4.2246 and I want to round this to 2 decimal places, the required result is 4.23. The solution given here will return 4.22 which is incorrect. The correct solution is to step through the number one digit at a time from right to left and if the number if greater than 4 than add 1 to the number on the left of it, then repeat for the next number.
about 2 years ago
I’m not sure why you are expecting 4.2246 to round to 4.23. For symmetric arithmetic rounding, the rule is that you only look at the number immediately to the right of the decimal place to which you are rounding. So, in the case of your example, if you wish to round 4.2246 to 2 decimal places, you look to the number immediately to the right of the second 2. If the number is less than 5, the number to the left remains the same. If the number is 5 or greater, you round up. In this case, the number immediately to the right of two decimal places is a 4, so the rounded number is 4.22. If you followed your suggestion of stepping through the numbers one digit at a time from right to left, then you run into unexpected results. Try it out in Excel and you’ll see that 4.2246 rounds to 4.23.
Think of pi, a number which has no definitive # of decimal places (i.e. it’s decimal representation never ends). Pi is commonly abbreviated as 3.14 when rounding to two decimal places. However, if you were to take your approach, which number farthest to the right would you start with? Since pi’s decimal representation repeats indefinitely, you have no number furthest to the right to start with.
Hope that helps.
about 2 years ago
Oops, you’re quite right. I have just learned that I have have rounded wrong all my life
….
about 1 year ago
Hi Adam,
I found one difference from your method to that of .Nets Math.Round.
Math.Round(0.7 – 0.05, 1, AwayFromZero) = 0.6
MathExt.Round(0.7 – 0.05, 1, AwayFromZero) = 0.7
Just curious what you would change in your method to make that work.
Aaron
about 1 year ago
@Aaron,
Sorry for the delay in my response.
The reason for the difference is that my MathExt implementation is only a decimal implementation. While the System.Math class has implementations for both decimal and double. When you call Math.Round, it automatically selects the appropriate overload (double or decimal) based on the passed type. So, calling Math.Round(0.7 – 0.05, 1, AwayFromZero) assumes a type of double while MathExt.Round(0.7 – 0.05, 1, AwayFromZero) always uses decimal. If you wanted Math.Round to match my implementation (decimal implementation), then you would need to explicitly specific decimal as the passed data type like this: Math.Round(0.7m – 0.05m, 1, AwayFromZero). Note the “m” next to 0.7 and 0.05, which tells the framework this is a decimal datatype and not the default double datatype. Math.Round(0.7m – 0.05m, 1, AwayFromZero) results in 0.7 just as MathExt does.
Now, if you want my MathExt helper to match the Math.Round double implementation, you would need to add additional overloads to it for the double datatype. Using reflector and opening up the System.Math class, you could pretty quickly add the double implementation. However, the current MathExt is purely a decimal implementation.
I hope that answers your question. Sorry for the confusion.
Let me know if you have any further questions.
Adam