Arithmetic Rounding in .Net

Quiz time: what is the value of test after the following code runs:

.csharpcode { font-size: 10pt; color: black; font-family: Courier New , Courier, Monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0px; } .rem { color: #008000; } .kwrd { color: #0000ff; } .str { color: #006080; } .op { color: #0000c0; } .preproc { color: #cc6633; } .asp { background-color: #ffff00; } .html { color: #800000; } .attr { color: #ff0000; } .alt { background-color: #f4f4f4; width: 100%; margin: 0px; } .lnum { color: #606060; }
.csharpcode { font-size: 10pt; color: black; font-family: Courier New , Courier, Monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0px; } .rem { color: #008000; } .kwrd { color: #0000ff; } .str { color: #006080; } .op { color: #0000c0; } .preproc { color: #cc6633; } .asp { background-color: #ffff00; } .html { color: #800000; } .attr { color: #ff0000; } .alt { background-color: #f4f4f4; width: 100%; margin: 0px; } .lnum { color: #606060; }
decimal test = decimal.Round(12.345m, 2);
 

12.35, right? No - imagine my surprise to find that it returns 12.34!! It turns out that .Net employs a type of rounding called "banker's rounding". When the rounding logic encounters a midpoint (like the 5 in my example), rather than always rounding up, it rounds to then nearest even number. Of course, in my situation, I needed the traditional "arithmetic rounding" which always goes up (or away from zero) when a midpoint is encountered. Finding no such feature in .Net, I wrote my own. If you see any bugs in it, let me know!  (Note: code updated based on input from Ramon and Edward)

public static decimal ArithmeticRound (decimal d, int decimals)

{

  decimal power = (decimal)Math.Pow(10, decimals);

  // Note: converting to positive number before calculating rounding, so that we don't need separate logic for negative number handling

  decimal result = decimal.Floor((Math.Abs(d) * power) + 0.5m) / power;

  return result * Math.Sign(d);

}

 
BTW, .Net 2 provides an option on the Round function to use either type of rounding. See http://msdn2.microsoft.com/library/wxhaazw6(en-us,vs.80).aspx.
Published Wed, Jun 15 2005 12:15 PM by Joshua Langemann

Comments

Thursday, June 16, 2005 3:13 AM by Joshua Langemann

# re: Arithmetic Rounding in .Net

uhm... adding a half before rounding will give the correct result.

So in this case

decimal.Round(12.345 + 0.005, 2)

Will give you 12.35

decimal.Round(12.345 + 0.5/(2*10), 2)

or another example

decimal.Round(12.345 + 0.5/(1*10), 1)

Seems alot more efficient then your implementation :-)
Thursday, June 16, 2005 4:37 AM by Joshua Langemann

# re: Arithmetic Rounding in .Net

Or something like...

private static decimal ArithmeticRound2(decimal d, int decimals)
{
decimal power = (decimal)Math.Pow(10, decimals);
// Note: converting to positive number before calculating rounding, so that we don't need separate logic for negative number handling
decimal result = (decimal)((Int64)((Math.Abs(d) * power) + (decimal)0.5) / power);
return result * Math.Sign(d);
}

Friday, June 17, 2005 6:18 PM by Joshua Langemann

# re: Arithmetic Rounding in .Net

Thanks for your input. That is nice and simple, Ramon, but rounding the number plus the addition of the midpoint will fail on situations like Round(12.250, 2), which would yield 12.26 instead of 12.25. Truncating the result of the addition works just fine and is indeed a lot simpler. Thanks for the working code snippet, Edward - I ended up with a slightly modified version:
public static decimal ArithmeticRound (decimal d, int decimals)
{
decimal power = (decimal)Math.Pow(10, decimals);
// Note: converting to positive number before calculating rounding, so that we don't need separate logic for negative number handling
decimal result = decimal.Floor((Math.Abs(d) * power) + 0.5m) / power;
return result * Math.Sign(d);
}

Leave a Comment

(required) 
(required) 
(optional)
(required) 
Please add 4 and 3 and type the answer here: