2016-09-13 72 views
3

How does DoubleUtil.DoubleToInt(double val) work?我们学习了.NET Framework有舍入浮点值的一种特殊的方式:为什么使用这种尴尬的方式将一个浮点数舍入为一个整数?

public static int DoubleToInt(double val) 
{ 
    return (0 < val) ? (int)(val + 0.5) : (int)(val - 0.5); 
} 

为什么他们不只是使用(int)Math.Round(val)

或者:为什么Math.Round这种方式没有定义,如果这是优越的?必须有一些权衡。

+0

因为'Math.Round'有重载处理多少个小数位来舍去和不同类型,将是我的猜测。 – juharr

回答

1

我可以看到这是一个优化,因为要从Round获得相同的行为,您需要使用MidpointRounding.AwayFromZero选项。从reference source这是实现通过:

private static unsafe double InternalRound(double value, int digits, MidpointRounding mode) { 
    if (Abs(value) < doubleRoundLimit) { 
     Double power10 = roundPower10Double[digits]; 
     value *= power10; 
     if (mode == MidpointRounding.AwayFromZero) {     
      double fraction = SplitFractionDouble(&value); 
      if (Abs(fraction) >= 0.5d) { 
       value += Sign(fraction); 
      } 
     } 
     else { 
      // On X86 this can be inlined to just a few instructions 
      value = Round(value); 
     } 
     value /= power10; 
    } 
    return value; 
} 

我只能猜测,该实用方法的作者做了一些性能对比。

+0

我只是基准变化:圆形(双)是140单位(但使用四舍五入银行家无人期望),Round(double,ToEven)是310单位,DoubleToInt是42单位和(int)截断是9单位。基准包括将结果添加到累加器以确保结果被消耗。所以它更快(但不正确)。 – usr

3

Math.Round将导致创建具有所需确切值的double,然后需要将其转换为int。这里的代码避免了创建那个double。它还允许错误处理的细节,以及与其他类型的舍入模式或数字相关的代码四舍五入。

+0

这也会创建一个中间双精度(是'val + 0.5'或'val-0.5' –

+0

@SimonByrne不同之处在于,执行常数值的添加要比执行计算以找出最接近的数字要容易得多整数不是“double”值的创建,而是计算出这个double值是多少。 – Servy

+0

嗯,硬件不应该同时支持截断和舍入?我不明白为什么会有一个成本差异可能是由验证引起的成本和检测到的数字轮到 – usr

2

它们在小数部分1/2处具有不同的行为。根据Math.Round

如果a的小数部分是两个整数之间的一半,其中一个是偶数,另一个是奇数,则返回偶数。

所以,如果val == 0.5,然后Math.Round(val) == 0.0,而这DoubleToInt会给(int)(0.5+0.5) == 1。换句话说,DoubleToInt离零点一圈1/2(类似标准C round函数)。

还有潜在这里不太期望的行为:如果val实际上是double之前 0.5(即0.49999999999999994)然后,取决于C#如何处理中间精密度,它实际上可能得​​到1-(如val + 0.5是不可由double表示,并可以四舍五入为1)。事实上,这在Java 6(及更早版本)中是an infamous specification bug

+1

请注意,使用MidpointRounding.AwayFromZero选项可以通过重载获得相同的行为。 –

+0

http://stackoverflow.com/a/24348037/122718是的,似乎是不正确的。 – usr

相关问题