开发者

Rounding double values in C#

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.


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 


double d = 1.2345;

Math.Round(d, 2);

the code above should do the trick.


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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜