[C#] Excel rounding functions Round, RoundUp, RoundDown C# version

I often miss the Round, RoundUp, RoundDown functions in Excel when rounding decimals in C#, the reason is that the latter is "grounded", more in line with my small people's rounding requirements, what "banker rounding method" let the banker use it. I had some time today to implement it, and first to refresh the functions of these Excel functions.

**Round(value, digits)**

Rounds value by rounding, retaining digits of decimal places; when digits is negative, it is rounded to the left of the decimal point; when value is negative, it behaves exactly the opposite of a positive number.

** Examples.**Round(3.145, 2) = 3.15；Round(-3.145, 2) = -3.15；Round(3145, -2) = 3100

**RoundUp(value, digits)**

Rounds value up in the direction away from 0, retaining digits of decimal places; when digits is negative, rounds to the left of the decimal point

** Examples.**RoundUp(3.111, 2) = 3.12；RoundUp(-3.111, 2) = -3.12；RoundUp(3111, -2) = 3200

**RoundDown(value, digits)**

Rounds value down in the direction near 0, retaining digits of decimal places; when digits are negative, round to the left of the decimal point

** Examples.**RoundDown(3.145, 2) = 3.14；RoundDown(-3.145, 2) = -3.14；RoundDown(3145, -2) = 3100

** Principle of implementation.**

- For RoundUp and RoundDown, since the Ceiling and Floor methods of the decimal or Math classes (hereafter referred to as C/F) can only be rounded, first multiply and divide to get the new value available for the C/F method to play with, depending on the number of digits to be retained, and then you can use C/F to get the rounded value and multiply/divide back to get the final result. This method is common on the market.

** Examples.** 1.114 is retained 2 places up, first 1.114x100 gives 111.4, then C(111.4) gives 112, then 112 / 100, and finally 1.12

** Question.** Since the original value has to be multiplied and divided first, this step causes an overflow for original values close to Max/Min, or with too much precision, so Up and Down cannot cope with particularly large values, but everyday applications are believed to be fine.

- For the RoundEx method, it wraps decimal directly. Round(decimal, MidpointRounding.AwayFromZero) to get the result.

** Description of implementation.**

- Provided as an extension method, compatible with the regular method call method (nonsense). That is, either 3.145M.RoundEx(2) or MathEx.RoundEx(3.145M, 2)

- Each method provides overloads in two types, decimal and double, for a total of six methods

- The implementation is based on the decimal type, the double version is just reused + type conversion. The reason for not implementing it for doubles is not because of laziness, but because floating point operations are prone to bullshit, e.g. 555.55x100=55554.99999999999993. For a discussion of the unreliability of floating-point operations, see.http://www.cnblogs.com/ethancai/articles/1237012.html

- The rounding function is named RoundEx because the decimal class already has a static method called Round, which, if not opened, will not be called in the extended way 3M.Round(). And although .net is very inclusive in naming, I think it's better to avoid FCL naming as much as possible, there's no need to "enjoy" this freedom

- The reason why several methods have to determine the number of reserved bits first, instead of directly using the digits subdivision of 10, is to try to follow the native methods of the decimal type and reduce unnecessary math. It's not minimalist code we're after, it's performance. Not tested, of course ~ eggs flying in...

A bunch of nonsense, on to the code.

/// <summary> /// Mathematics class extension methods /// </summary> public static class MathEx { /// <summary> /// keep away from 0 round up /// </summary> public static decimal RoundUp(this decimal value, sbyte digits) { if (digits == 0) { return (value >= 0 ? decimal.Ceiling(value) : decimal.Floor(value)); } decimal multiple = Convert.ToDecimal(Math.Pow(10, digits)); return (value >= 0 ? decimal.Ceiling(value * multiple) : decimal.Floor(value * multiple)) / multiple; } /// <summary> /// close 0 round down /// </summary> public static decimal RoundDown(this decimal value, sbyte digits) { if (digits == 0) { return (value >= 0 ? decimal.Floor(value) : decimal.Ceiling(value)); } decimal multiple = Convert.ToDecimal(Math.Pow(10, digits)); return (value >= 0 ? decimal.Floor(value * multiple) : decimal.Ceiling(value * multiple)) / multiple; } /// <summary> /// round up to the nearest integer /// </summary> public static decimal RoundEx(this decimal value, sbyte digits) { if (digits >= 0) { return decimal.Round(value, digits, MidpointRounding.AwayFromZero); } decimal multiple = Convert.ToDecimal(Math.Pow(10, -digits)); return decimal.Round(value / multiple, MidpointRounding.AwayFromZero) * multiple; } /// <summary> /// keep away from 0 round up /// </summary> public static double RoundUp(this double value, sbyte digits) { return decimal.ToDouble(Convert.ToDecimal(value).RoundUp(digits)); } /// <summary> /// close 0 round down /// </summary> public static double RoundDown(this double value, sbyte digits) { return decimal.ToDouble(Convert.ToDecimal(value).RoundDown(digits)); } /// <summary> /// round up to the nearest integer /// </summary> public static double RoundEx(this double value, sbyte digits) { return decimal.ToDouble(Convert.ToDecimal(value).RoundEx(digits)); } }

** - Man Bi -**