C#中保留两位小数的四舍五入

42

我有一个十进制数,可能像以下这样:

189.182

我想将其向上舍入到2位小数,因此输出应该是以下内容:

  

189.19

在Math类或其他地方是否有内置的功能可以实现此功能? 我知道ceiling函数存在,但它似乎不能实现我的要求- 它会四舍五入到最近的整数,所以在这种情况下只是'189'。


4
为什么标记为重复?原帖要求向上取整,而其他问题要求向下取整。 - JC.
不离开小数类型:`public static decimal RoundUp(decimal input, int places) { if (places < 0) return input; decimal multiplier = 1; for (int i = 0; i < places; ++i) multiplier *= 10; return (Math.Ceiling(input * multiplier) / multiplier); }` - Steve Hibbert
9个回答

61

乘以100,向上取整,再除以100可以得到我认为你要求的结果。

public static double RoundUp(double input, int places)
{
    double multiplier = Math.Pow(10, Convert.ToDouble(places));
    return Math.Ceiling(input * multiplier) / multiplier;
}

用法示例:

RoundUp(189.182, 2);

这个方法通过将小数点向右移动两位(使其位于最后一个8的右侧),然后执行上取整操作,最后将小数点移回到原来的位置。


14
这个解决方案在技术上是不正确的。一个双精度浮点数有一个尾数和一个指数,而指数是以二进制而非十进制为基数的。将一个双精度浮点数乘以10或100并不能简单地移动小数点。乘法后的结果具有不同的位模式。由于这个结果可能无法完全精确地作为二进制数字放入尾数中,所以可能会被截断,此时最后一位有效位将根据某些规则进行四舍五入。 - tomosius
1
但对于像 189.182 这样的数字,你应该是相当安全的。也许应该转换为十进制? - Simon_Weaver
5
简单的例子,在十进制中失败 https://dotnetfiddle.net/6IeRVG 输入283.79,输出283.8。 - fiat
1
请注意上面的警告。对于某些数字,此方法会产生意想不到的结果。 - userSteve

36

您可以使用:

decimal n = 189.182M;
n = System.Math.Ceiling (n * 100) / 100;

这里可以找到有关各种舍入函数的解释


请注意,像这样的公式仍受限于double类型的有限精度,如果您使用的是该类型(虽然您的问题陈述了十进制数,但您可能只是指具有分数部分的浮点值而不是具体类型)。

例如:

double n = 283.79;
n = System.Math.Ceiling (n * 100);

实际上将会返回28380,而不是你预期的283.79(a)

如果你想在所有情况下得到准确的结果,你一定要使用decimal类型。


(a)这是因为283.79的最准确的IEEE754双精度表示实际上是:

 283.790000000000020463630789891

那个额外的(尽管微不足道的)小数部分超过了.79,它将被上取整,这意味着它会给你一个比你预期的值更高的结果。


2
这应该是被采纳的答案,因为当前被采纳的回答没有解决重要的浮点精度问题。 - Ben Wilde
99.10 x 403.6326得到 40000 预期 39999.99 - undefined

9
在 .NET Core 3.0 及更高版本中,通过MidpointRounding枚举,提供了三种额外的舍入策略。 除了MidpointRounding.AwayFromZero和MidpointRounding.ToEven之外,现在还包括:
 1. MidpointRounding.ToNegativeInfinity
 2. MidpointRounding.ToPositiveInfinity
 3. MidpointRounding.ToZero

针对这个问题,需要使用MidpointRounding.ToPositiveInfinity,这将始终将数字向上舍入。请注意,仅当数字不为负数时才有效。请参见下表中的示例。
原始数字 ToNegativeInfinity ToPositiveInfinity ToZero
3.55 3.5 3.6 3.5
2.83 2.8 2.9 2.8
2.54 2.5 2.6 2.5
2.16 2.1 2.2 2.1
-2.16 -2.2 -2.1 -2.1
-2.54 -2.6 -2.5 -2.5
-2.83 -2.9 -2.8 -2.8
-3.55 -3.6 -3.5 -3.5
有关midpointrounding的更多信息,请参见此处
当然还有使其工作的代码:
// function explained: Math.Round(number, amount of decimals, MidpointRounding);
decimal number = 189.182m;
number =  Math.Round(number, 2, MidpointRounding.ToPositiveInfinity);
// result: number = 189.19

5
            var numberToBeRound1 = 4.125;
            var numberToBeRound2 = 4.175;
            var numberToBeRound3 = 4.631;
            var numberToBeRound4 = 4.638;
            var numberOfDecimalPlaces = 2;
            var multiplier = Math.Pow(10, numberOfDecimalPlaces);

            //To Round Up => 4.13
            var roundedUpNumber = Math.Ceiling(numberToBeRound1 * multiplier) / multiplier;

            //To Round Down => 4.12
            var roundedDownNumber = Math.Floor(numberToBeRound1 * multiplier) / multiplier;

            //To Round To Even => 4.12
            var roundedDownToEvenNumber = Math.Round(numberToBeRound1, numberOfDecimalPlaces, MidpointRounding.ToEven);

            //To Round To Even => 4.18
            var roundedUpToEvenNumber = Math.Round(numberToBeRound2, numberOfDecimalPlaces, MidpointRounding.ToEven);

            //To Round To Away From Zero => 4.63
            var roundedDownToAwayFromZero = Math.Round(numberToBeRound3, numberOfDecimalPlaces, MidpointRounding.AwayFromZero);

            //To Round To Away From Zero => 4.64
            var roundedUpToAwayFromZero2 = Math.Round(numberToBeRound4, numberOfDecimalPlaces, MidpointRounding.AwayFromZero);

我没有注意到问题的年代,但我已经尽力表达得非常清楚了。 - Furkan Öztürk
1
不要像 Pow 一样,要像 Furkan 一样! - Csaba Toth
2
Math.Round(number, decimalPlaces, MidpointRounding.AwayFromZero); - Csaba Toth

4
如何?
0.01 * ceil(100 * 189.182)

太棒了,正是我所需要的。我不知道为什么Math.ceil(189.182 * 100) / 100不起作用... - Kevin Vella
只是想要为其他遇到同样问题的人加一句,要获得更多或更少的小数位,请调整常量中零的数量——3个小数位为0.001 * ceil(1000 * n),4个小数位为0.0001 * ceil(10000 * n),以此类推。 - Robin French

1
    public static decimal RoundUp(decimal input, int places)
    {
        decimal multiplier = (decimal)Math.Pow(10, places);
        return decimal.Ceiling(input * multiplier) / multiplier;
    }

0
另一种古怪但有趣的方法是在偏移数字后使用Math.Round()
decimal RoundUp(decimal n, int decimals)
{
    n += decimal.Parse($"1e-{decimals}", System.Globalization.NumberStyles.AllowExponent) / 2;
    n -= 1e-28m;
    return Math.Round(n, decimals);
}

decimal RoundDown(decimal n, int decimals)
{
    n -= decimal.Parse($"1e-{decimals}", System.Globalization.NumberStyles.AllowExponent) / 2;
    n += 1e-28m;
    return Math.Round(n, decimals);
}

它的优点是不使用Math.Pow(),后者使用double,因此可能会导致不可预测的舍入误差。

这个解决方案基本上使用了中点舍入可以通过稍微增加/减少数字来转换为上下舍入的事实:

  • Math.Round(3.04m, 1)3.0 - 不是我们想要的
  • 让我们加上0.04(9)
  • Math.Round(3.0899999999999999999999999999m, 1)3.1 - 成功了!

减去1e-28m(= 0.0000000000000000000000000001)很重要,因为我们希望能将3.0000000000000000000000000001四舍五入成4,但3.0000000000000000000000000000应该保持为3


0
   // Double will return the wrong value for some cases. eg: 160.80
    public static decimal  RoundUp(decimal input, int places)
    {
        decimal multiplier = Convert.ToDecimal(Math.Pow(10, Convert.ToDouble(places)));
        return Math.Ceiling(input * multiplier) / multiplier;
    }

请在您的回答中始终描述您正在做什么。它应该被更新或删除。在提供更多答案之前,请阅读如何回答。^^ - finnmglas

0
Math.Round(price, 2, MidpointRounding.AwayFromZero);

帮助了我
.NET 4.5
检查了计算结果:
99.10 × 403.6326 = 39999.99;
2.065 × 277 = 572.01;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接