在 C# 中舍入双精度值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2129804/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 23:43:21  来源:igfitidea点击:

Rounding double values in C#

c#doublerounding

提问by Steve

I want a rounding method on double values in C#. It needs to be able to round a double value to any rounding precision value. My code on hand looks like:

我想在 C# 中对双精度值进行舍入。它需要能够将 double 值舍入为任何舍入精度值。我手头的代码如下所示:

public static double RoundI(double number, double roundingInterval) {

    if (roundingInterval == 0.0)
    {
        return;
    }

    double intv = Math.Abs(roundingInterval);
    double sign = Math.Sign(number);
    double val = Math.Abs(number);

    double valIntvRatio = val / intv;
    double k = Math.Floor(valIntvRatio);
    double m = valIntvRatio - k;

    bool mGreaterThanMidPoint = ((m - 0.5) >= 1e-14) ? true : false;
    bool mInMidpoint = (Math.Abs(m - 0.5) < 1e-14) ? true : false;
    return (mGreaterThanMidPoint || mInMidpoint) ? sign * ((k + 1) * intv) : sign * (k * intv);
}

So RoundI(100, 3) should give 99 and RoundI(1.2345, 0.001) should give 1.235.

所以 RoundI(100, 3) 应该给出 99 而 RoundI(1.2345, 0.001) 应该给出 1.235。

The problem is, RoundI(1.275, 0.01) returns 1.27, rather than 1.28. This is because when executing double valIntvRatio = val/intv, that is, double valIntvRatio = 1.275 / 0.01, it gives 0.12749999999999. I know this is a problem with double representation in any programming language. My question is, is there a standard code to do things like this, without the need to worry about precision on double? Here I set the tolerant to 1e-14, but this is too restrict for this problem and I don't know what is the correct tolerance to be set. Thank you for any help.

问题是,RoundI(1.275, 0.01) 返回 1.27,而不是 1.28。这是因为在执行 double valIntvRatio = val/intv,即 double valIntvRatio = 1.275 / 0.01 时,它给出了 0.12749999999999。我知道这是任何编程语言中双重表示的问题。我的问题是,是否有标准代码可以做这样的事情,而无需担心 double 的精度?在这里,我将容忍度设置为 1e-14,但这对这个问题来说太严格了,我不知道要设置的正确容忍度是多少。感谢您的任何帮助。

采纳答案by Jimmy

Example of using decimal, as Kibbeepointed out

使用示例decimal,正如Kibbee指出的

double d = 1.275;
Math.Round(d, 2);          // 1.27
Math.Round((decimal)d, 2); // 1.28 

回答by AceMark

double d = 1.2345;

Math.Round(d, 2);

the code above should do the trick.

上面的代码应该可以解决问题。

回答by Jonas Elfstr?m

If you actually need to use doublejust replace it below and it will work but with the usual precision problems of binary floating-point arithmetics.

如果您确实需要使用,double只需在下面替换它,它就可以工作,但通常会遇到二进制浮点运算的精度问题。

There's most certainly a better way to implement the "rounding" (almost a kind of bankers' rounding) than my string juggling below.

肯定有比我在下面玩杂耍更好的方法来实现“舍入”(几乎是一种银行家的舍入)。

public static decimal RoundI(decimal number, decimal roundingInterval)
{
   if (roundingInterval == 0) { return 0;}

   decimal intv = Math.Abs(roundingInterval);
   decimal modulo = number % intv;
   if ((intv - modulo) == modulo) {
       var temp = (number - modulo).ToString("#.##################");
       if (temp.Length != 0 && temp[temp.Length - 1] % 2 == 0) modulo *= -1;
   }
    else if ((intv - modulo) < modulo)
        modulo = (intv - modulo);
    else
        modulo *= -1;

    return number + modulo;
}

回答by R.T.

The examples using decimal casting provided in Jimmy's answer don't answer the question, since they do not show how to round a double value to any rounding precision value as requested. I believe the correct answer using decimal casting is the following:

Jimmy的回答中提供的使用十进制转换的示例没有回答这个问题,因为它们没有显示如何根据要求将 double 值舍入到任何舍入精度值。我相信使用十进制转换的正确答案如下:

    public static double RoundI(double number, double roundingInterval)
    {
        return (double)((decimal)roundingInterval * Math.Round((decimal)number / (decimal)roundingInterval, MidpointRounding.AwayFromZero));
    }

Because it uses decimal casting, this solution is subject to the casting errors mentioned by Jeppe Stig Nielsenin his comment to Jimmy's answer.

因为它使用十进制转换,所以这个解决方案会受到Jeppe Stig Nielsen在他对Jimmy回答的评论中提到的转换错误的影响。

Also, note that I specified MidpointRounding.AwayFromZero, since that is consistent with the requester's specification that RoundI(1.2345, 0.001) should give 1.235.

另外,请注意我指定了 MidpointRounding.AwayFromZero,因为这与请求者的规范一致,RoundI(1.2345, 0.001) 应该给出 1.235。