有没有办法在.NET中进行'正确'的算术舍入? / C#

时间:2010-03-25 11:07:44

标签: c# .net math rounding bankers-rounding

我正在尝试将一个数字舍入到它的第一个小数位,考虑到不同的MidpointRounding选项,这似乎运作良好。但是,当该数字具有随后的小数位数时会出现问题,这些小数位数会在算术上影响舍入。

一个例子:

0.10.11..0.190.141..0.44有效:

Math.Round(0.1, 1) == 0.1
Math.Round(0.11, 1) == 0.1
Math.Round(0.14, 1) == 0.1
Math.Round(0.15, 1) == 0.2
Math.Round(0.141, 1) == 0.1

但使用0.141..0.149时,0.1始终返回0.146..0.149,但0.2应该转到Math.Round(0.145, 1, MidpointRounding.AwayFromZero) == 0.1 Math.Round(0.146, 1, MidpointRounding.AwayFromZero) == 0.1 Math.Round(0.146, 1, MidpointRounding.ToEven) == 0.1 Math.Round(0.146M, 1, MidpointRounding.ToEven) == 0.1M Math.Round(0.146M, 1, MidpointRounding.AwayFromZero) == 0.1M

0.144449

我尝试提出一个解决这个问题的函数,它适用于这种情况,但当然如果你试图将0.2舍入到它的第一个十进制数字(应该是0.1,但结果private double "round"(double value, int digit) { // basically the old "add 0.5, then truncate to integer" trick double fix = 0.5D/( Math.Pow(10D, digit+1) )*( value >= 0 ? 1D : -1D ); double fixedValue = value + fix; // 'truncate to integer' - shift left, round, shift right return Math.Round(fixedValue * Math.Pow(10D, digit)) / Math.Pow(10D, digit); } 。)(这也不适用于Math.Round()。)

{{1}}

我假设一个解决方案是枚举所有数字,找到大于4的第一个值然后向上舍入,否则向下舍入。问题1:这似乎是愚蠢的,问题2:我不知道如何在没有大量乘法和减法的情况下枚举数字。

长话短说:最好的方法是什么?

4 个答案:

答案 0 :(得分:20)

Math.Round()表现正常。

当您执行标准中点舍入时,您永远不需要超出您要四舍五入的位置之外的1位十进制数。如果你四舍五入到最接近的十分之一,那么你永远不需要超越小数点后的第二个数字。

中点四舍五入的想法是,中间数字的一半应该向上舍入,一半应该向下舍入。因此对于介于0.1和0.2之间的数字,其中一半应该舍入到0.1,一半应该舍入到0.2。这两个数字之间的中点是0.15,因此这是四舍五入的阈值。 0.146小于0.15,因此必须向下舍入到0.1。

                    Midpoint
0.10                  0.15                  0.20
 |----------------|----|---------------------|
                0.146
       <---- Rounds Down

答案 1 :(得分:15)

我不知道你想要在这里完成什么。 0.149舍入到一个小数位 0.1,而不是0.2

答案 2 :(得分:6)

舍入不是一个迭代过程,只能绕一次。

所以0.146四舍五入到1位十进制数 0.1。

你不这样做:

0.146 --> 0.15
0.15 -->  0.2

你只这样做:

0.146 --> 0.1

否则,以下内容:

0.14444444444444446

也会变为0.2,但它不会,也不应该。

答案 3 :(得分:5)

不要尝试复合舍入'错误'。这就是你要做的。

.146 应该向下舍入到.1,如果你要到一个小数位。

首先将其四舍五入为.15,然后再将其四舍五入为.2,您只是引入了更多的舍入误差,而不是更少。