在C中舍入double值#

问题描述:

我想在C#中使用double值的舍入方法。它需要能够将double值舍入到任何舍入精度值。我手头上的代码如下所示:在C中舍入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,但这对于这个问题太严格了,我不知道要设置的正确容差是多少。感谢您的任何帮助。

+5

也许你应该考虑使用十进制数据类型。 – Kibbee 2010-01-25 02:03:48

+0

为什么轮(100,3)给99?如果你四舍五入到与3(0位)相同的分数位置,你会得到100,而不是99. – paxdiablo 2010-01-25 02:17:37

+0

paxdiablo:抱歉,RoundI的目的不是将第一个参数舍入到相同的小数位作为第二个参数。第二个参数是循环间隔,舍入将第一个参数舍入到具有模式0的第一个参数到第二个参数。 – Steve 2010-01-25 03:37:28

使用decimal,作为基比的实施例指出

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

虽然这个伟大的解决方案通常有效,但当'd'的有效数字接近'double'的精度限制时,转换为'decimal'实际上消除了太多的精度。作为一个例子,用'd = 123456789.256;'(你需要用'd.ToString(“R”)'输出这个来揭示“隐藏的”精度)。如果你在这个例子中简单地使用'Math.Round(d,2)'(记得用'“R”'写出结果),你会得到比'Math.Round((decimal)d,2' )'。这种转换为“decimal”的方法在这里删除了太多的精度。在这种情况下,从头开始使用'decimal',不会强制转换。 – 2014-01-02 09:36:13

+0

+1非常好的解决方案,但不幸的是它不会将** 0.005 **转换为** 0.01 **。结果是** 0 ** – 2014-01-02 16:53:01

+8

@ Zefnus因为它使用了银行家的四舍五入。如果你想把它舍入到0.01,使用'Math.Round((十进制)d,2,MidpointRounding.AwayFromZero)' – Jimmy 2014-01-02 21:06:47

double d = 1.2345; 

Math.Round(d, 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; 
} 
+0

真棒... fanks ...救了我一些工作,一个出来。 – Jon 2011-11-03 20:59:58

+0

可能发现了一个错误。如果传入:number = 0.5舍入间隔1.0事情var var数组位有点错误。 – Jon 2011-11-04 19:30:41

+0

这是真的,但比数字更大的间隔没有任何意义,是吗?我在该方法中添加了一个额外的检查。 – 2011-11-04 22:01:50

使用吉米的答案提供小数铸造不回答这个问题,因为他们没有说明如何将双值四舍五入到任何舍入精度值要求的例子。我相信使用十进制铸造正确的答案是:

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

因为它使用十进制铸造,该解决方案是受叶普斯蒂格在他的吉米的回答则留言中提到尼尔森铸造错误。

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