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.