6

我正在尝试将一个数字四舍五入到小数点后的第一位,并且考虑到不同的 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

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

我试图想出一个解决这个问题的函数,它在这种情况下效果很好,但是如果你尝试将 ie 四舍五入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);
}

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

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

4

4 回答 4

20

Math.Round()行为正确。

中点舍入的想法是中间数字的一半应该向上舍入,一半应该向下舍入。因此,对于 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
于 2010-03-25T11:16:02.913 回答
15

我不明白你想在这里完成什么。0.149 四舍五入到小数点后一位0.1,而不是 0.2

于 2010-03-25T11:10:41.133 回答
6

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

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

你不这样做:

0.146 --> 0.15
0.15 -->  0.2

你只能这样做:

0.146 --> 0.1

否则,以下内容:

0.14444444444444446

也会四舍五入到 0.2,但它没有,也不应该。

于 2010-03-25T11:23:35.847 回答
5

不要试图复合舍入“错误”。这就是你想要做的。

如果要保留一位小数, 0.146应该向下舍入到 0.1。

首先将其舍入为 0.15,然后再舍入为 0.2,您只是引入了更多的舍入误差,而不是更少。

于 2010-03-25T11:16:52.320 回答