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

sun*_*ide 6 .net c# math bankers-rounding rounding

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

一个例子:

随着0.1,0.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
Run Code Online (Sandbox Code Playgroud)

但随着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
Run Code Online (Sandbox Code Playgroud)

我试图提出一个解决这个问题的函数,并且它适用于这种情况,但当然如果你尝试舍入即0.144449它的第一个十进制数字(应该是0.2,但结果0.1)它会很好地失败.(那不是也不能使用Math.Round().)

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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

Ste*_*ngs 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
Run Code Online (Sandbox Code Playgroud)


ano*_*ard 15

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

  • 我认为现在需要浓咖啡.:D谢谢任何人! (4认同)
  • 是的,你没有从最后一个小数位开始舍入,然后工作.你只需查看要舍入的地方后面的小数位. (3认同)
  • 如果1.49 - > 0.15 - > 0.2你四舍五入!一次到2dp和一次到1dp.你通常不希望四舍五入,因为每一轮都会给"真实"值增加误差 (2认同)

ang*_*son 6

舍入不是一个迭代过程,你只绕一次.

所以0.146舍入到1个十进制数 0.1.

你不这样做:

0.146 --> 0.15
0.15 -->  0.2
Run Code Online (Sandbox Code Playgroud)

你只这样做:

0.146 --> 0.1
Run Code Online (Sandbox Code Playgroud)

否则,以下内容:

0.14444444444444446
Run Code Online (Sandbox Code Playgroud)

也会圆到0.2,但它不会,也不应该.


dav*_*ere 5

不要试图复合舍入'错误'.这是你想要做的.

如果你要去小数点后一位,.146 应该向下舍入到.1.

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