Rounding double values in C#

2019-01-14 11:05发布

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:

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.

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.

4条回答
劫难
2楼-- · 2019-01-14 11:11

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

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;
}
查看更多
趁早两清
3楼-- · 2019-01-14 11:16
double d = 1.2345;

Math.Round(d, 2);

the code above should do the trick.

查看更多
等我变得足够好
4楼-- · 2019-01-14 11:22

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:

    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 Nielsen in his comment to Jimmy's answer.

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.

查看更多
Juvenile、少年°
5楼-- · 2019-01-14 11:31

Example of using decimal, as Kibbee pointed out

double d = 1.275;
Math.Round(d, 2);          // 1.27
Math.Round((decimal)d, 2); // 1.28 
查看更多
登录 后发表回答