很多人都知道Math.Round()是C#中用来做四舍五入,保留指定小数位的。但实际上它并不是真正的四舍五入,而是银行家算法的“四舍六入五取偶”,事实上这也是IEEE的规范,因此所有符合IEEE标准的语言都应该采用这样的算法。其规则如下:
- 1、当舍去位的数值小于5时:直接舍去
- 2、当舍去位的数值大于6时:进位加1
- 3、当舍去位的数值等于5时,分两种情况:
(1)若5后面有其他非0数字(即5不是最后一位)时,进位加1。
(2)若5后面只有0(即5是最后一位)时,则根据5的前一位的奇偶来判断,前一位为奇数则进位加1,为偶数则舍去。
遇到5需要舍去的情况只有一种,即5是最后一位有效数且前一位数是偶数。数字的精度越大,则这个算法就越像真正的四舍五入。
我们使用这个函数时,用来处理的数字通常是那些有n位小数的数字,而我们用于显示的通常也就只有2-4位,所以这也就不容易发现这个问题。
可能纯文字描述不太好理解,举几个例子:
Math.Round(1.14, 1); //结果:1.1
Math.Round(1.25, 1); //结果:1.2 五是最后一位且前一位为偶数,也舍去
Math.Round(1.15, 1); //结果:1.2 五是最后一位但前一位为奇数,进位加一
Math.Round(1.16, 1); //结果:1.2
如果希望实现中国式的标准的“四舍五入”法,应该怎么办呢?
从.NET 2.0 开始,Math.Round 方法提供了一个枚举选项--MidpointRounding.AwayFromZero,可以用来实现传统意义上的"四舍五入"。
即:
Math.Round(4.5); //结果为 4
Math.Round(4.5, MidpointRounding.AwayFromZero); //结果为 5
结果为 5。
MSDN文档中这个MidpointRounding.AwayFromZero的描述是“当一个数字是其他两个数字的中间值时,会将其舍入为两个值中绝对值较小的值。”
实际上从 .NET 2.0 发布以来,网上已经有很多人都指出了这个翻译错误(原文实际是 When a number is halfway between two others, it is rounded toward the nearest number that is away from zero.),实际上应该是取绝对值较大的值。可是很遗憾,.NET 3.0, .NET 3.5 的文档中,这个翻译错误依然没有改正过来。