在C#中四舍五入双精度数值
在C#中四舍五入双精度数值
我想在C#中使用一个方法对double值进行四舍五入。它需要能够将double值四舍五入到任意的精度值。我手头的代码如下:\n
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); }
\n所以RoundI(100, 3)应该返回99,RoundI(1.2345, 0.001)应该返回1.235。\n问题是,RoundI(1.275, 0.01)返回的是1.27,而不是1.28。这是因为在执行double valIntvRatio = val/intv时,即double valIntvRatio = 1.275 / 0.01,它给出的是0.12749999999999。我知道这是任何编程语言中double表示的问题。我的问题是,是否有一个标准的代码可以做到这样的事情,而不需要担心double的精度?在这里,我将容忍度设置为1e-14,但这对于这个问题来说太严格了,我不知道应该设置什么正确的容忍度。谢谢任何帮助。
问题的原因是使用double数据类型进行四舍五入操作时,会出现二进制浮点运算精度问题。解决方法是使用decimal数据类型进行四舍五入操作。
以下是解决问题的代码示例:
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; }
在上述代码中,通过使用decimal数据类型,可以避免二进制浮点运算精度问题。通过对输入的数字进行四舍五入的运算,确保结果的准确性。
在C#中,对double类型的值进行四舍五入可能会遇到问题。以上代码中,我们使用Math.Round()来将变量d的值四舍五入到小数点后两位。
然而,实际运行代码后可能会发现,结果并不是我们所期望的。原因在于Math.Round()方法的工作方式。
Math.Round()方法有多个重载,其中一个重载允许我们指定保留的小数位数。但是,该方法默认使用的是"银行家舍入法"(Banker's Rounding),这种舍入方式会根据要舍入的数字的最后一位和要舍入到的小数位数的下一位来判断舍入的结果。
具体来说,如果要舍入的数字的最后一位小于5,那么舍入结果将向下取整;如果最后一位大于5,那么舍入结果将向上取整;如果最后一位等于5,那么舍入结果将根据要舍入到的小数位数的下一位来决定。如果下一位是奇数,舍入结果向上取整;如果下一位是偶数,舍入结果向下取整。
在以上的代码中,d的值为1.2345,我们期望将其舍入到小数点后两位。根据银行家舍入法,最后一位数字5需要根据下一位数字4的奇偶性来决定舍入结果。因为下一位是偶数,所以舍入结果应该是向下取整,即1.23。
然而,实际运行代码后发现,结果却是1.24,与我们的期望不符。这是因为在某些情况下,Math.Round()方法会进行另一种舍入方式,即"对称舍入法"(MidpointRounding.ToEven)。这种舍入方式与银行家舍入法类似,但是当要舍入的数字的最后一位等于5时,舍入结果将向最接近的偶数方向取整。
要解决这个问题,我们可以使用Math.Round()方法的另一个重载,该重载允许我们指定舍入方式。我们可以将舍入方式设置为"AwayFromZero",这样就可以确保舍入结果始终向远离零的方向取整。
修改以上代码如下:
double d = 1.2345; Math.Round(d, 2, MidpointRounding.AwayFromZero);
通过指定舍入方式为MidpointRounding.AwayFromZero,我们可以得到我们期望的结果1.23。
总结起来,当在C#中对double类型的值进行四舍五入时,可能会遇到舍入结果与期望不符的问题。这是因为Math.Round()方法默认使用的是银行家舍入法,而不是我们常常使用的四舍五入规则。为了解决这个问题,我们可以使用Math.Round()方法的重载,并指定舍入方式为"AwayFromZero"来确保舍入结果符合我们的预期。
在C#中,对double类型的值进行四舍五入时,可能会出现精度问题。解决这个问题的方法是使用decimal类型进行计算。
在上述例子中,当对double类型的值进行四舍五入时,可以使用Math.Round方法。例如,对于double类型的值1.275,使用Math.Round(d, 2)会得到1.27的结果,而使用Math.Round((decimal)d, 2)会得到1.28的结果。
然而,当double类型的值的有效数字接近double类型精度的限制时,将其转换为decimal类型会丢失太多的精度。例如,当d = 12345678901234.256时(需要使用d.ToString("R")输出来显示“隐藏”的精度),如果在这个例子中简单地使用Math.Round(d, 2),会得到比使用Math.Round((decimal)d, 2)更好的结果。在这种情况下,使用decimal类型进行计算而不进行转换会保留更多的精度。
另外,如果希望将0.005四舍五入到0.01,可以使用Math.Round((decimal)d, 2, MidpointRounding.AwayFromZero)。因为默认情况下,Math.Round使用的是银行家舍入法,这会导致0.005被舍入为0。
至于为什么0.125的结果是0.12,没有提供具体的解释。