【C#】Excel舍入函数Round、RoundUp、RoundDown的C#版

本人在C#中进行小数舍入的时候经常会怀念Excel中的Round、RoundUp、RoundDown这几个函数,缘由就是后者“接地气”,比较符合俺小老百姓的舍入要求,啥“银行家舍入法”就让银行家用去吧。今儿有空,就把它实现了一下,先温习一下这几个Excel函数的功能:html

Round(value, digits)git

将value按四舍五入法进行舍入,保留digits位小数;当digits为负时,在小数点左侧进行舍入;当value为负时,表现与正数彻底相反。函数

举例:Round(3.145, 2) = 3.15;Round(-3.145, 2) = -3.15;Round(3145, -2) = 3100性能

RoundUp(value, digits)测试

按远离 0 的方向,将value向上舍入,保留digits位小数;当digits为负时,在小数点左侧进行舍入this

举例:RoundUp(3.111, 2) = 3.12;RoundUp(-3.111, 2) = -3.12;RoundUp(3111, -2) = 3200spa

RoundDown(value, digits).net

按靠近 0 的方向,将value向下舍入,保留digits位小数;当digits为负时,在小数点左侧进行舍入code

举例:RoundDown(3.145, 2) = 3.14;RoundDown(-3.145, 2) = -3.14;RoundDown(3145, -2) = 3100htm

实现原理:

- 对于RoundUp和RoundDown,因为decimal或Math类的Ceiling和Floor方法(下称C/F)只能取整,因此先根据要保留的位数,乘除获得可供C/F方法发挥的新值,而后就能够利用C/F获得舍入后的值,再乘/除回去,获得最终结果。此法市面常见。

举例:1.114向上保留2位,首先1.114x100获得111.4,再用C(111.4)获得112,而后112 / 100,最终获得1.12

问题:因为要先对原值进行乘除,因此对于接近Max/Min、或精度太高的原值,这一步就会形成溢出,因此Up和Down不能应对特别大的值,但平常应用相信没问题。

- 对于RoundEx方法,则直接封装decimal.Round(decimal, MidpointRounding.AwayFromZero)获得结果。

实现说明:

- 以扩展方法提供,兼容常规方法调用方式(废话)。便可以3.145M.RoundEx(2),也能够MathEx.RoundEx(3.145M, 2)

- 每一个方法以decimal和double两种类型提供重载,共6个方法

- 以decimal类型为基础进行实现,double版只是重用+类型转换。之因此不对double进行实现,不是由于偷懒,而是由于浮点运算容易扯蛋,如555.55x100=55554.999999999993。关于浮点运算的不可靠性,可参看:http://www.cnblogs.com/ethancai/articles/1237012.html

- 四舍五入函数命名为RoundEx是由于decimal类已经存在一个叫Round的静态方法,若是不错开,将不能以扩展方式3M.Round()进行调用。并且虽然.net在命名上具备极大的包容度,但我认为仍是尽可能避开FCL命名的好,无谓去“享受”这种自由度

- 几个方法之因此都要先判断一下保留位数,而没有直接使用10的digits次方进行运算,是想尽可能沿用decimal类型的原生方法,减小不必的数学运算。咱追求的不是极简的代码,而是性能。固然,没测试过~鸡蛋飞来中...

废话了一堆,上代码:

/// <summary>
/// 数学类扩展方法 /// </summary>
public static class MathEx { /// <summary>
    /// 远离 0 向上舍入 /// </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>
    /// 靠近 0 向下舍入 /// </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>
    /// 四舍五入 /// </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>
    /// 远离 0 向上舍入 /// </summary>
    public static double RoundUp(this double value, sbyte digits) { return decimal.ToDouble(Convert.ToDecimal(value).RoundUp(digits)); } /// <summary>
    /// 靠近 0 向下舍入 /// </summary>
    public static double RoundDown(this double value, sbyte digits) { return decimal.ToDouble(Convert.ToDecimal(value).RoundDown(digits)); } /// <summary>
    /// 四舍五入 /// </summary>
    public static double RoundEx(this double value, sbyte digits) { return decimal.ToDouble(Convert.ToDecimal(value).RoundEx(digits)); } }

- 文毕 -

相关文章
相关标签/搜索