在C#中舍入double值

时间:2010-01-25 02:00:51

标签: c# double rounding

我想在C#中对double值进行舍入方法。它需要能够将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);
}

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

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

4 个答案:

答案 0 :(得分:42)

使用decimal的示例, Kibbee 指出

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

答案 1 :(得分:5)

double d = 1.2345;

Math.Round(d, 2);

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

答案 2 :(得分:2)

如果你真的需要使用double,只需在下面替换它,它就可以工作,但是通常存在二进制浮点算术的精度问题。

最有可能实现“四舍五入”(几乎是一种银行家的四舍五入)的方式比我下面的字符串杂耍更好。

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;
}

答案 3 :(得分:2)

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

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

因为它使用了十进制转换,所以此解决方案受到 Jeppe Stig Nielsen Jimmy 的答案评论中提到的投射错误的影响。

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